You need to get the Secured Overnight Financing Rate (SOFR) into a PHP application reliably, with clean JSON, and enough metadata to drive loan pricing, dashboards, and risk models. By the end of this guide, you will query SOFR from the Interest Rates API, read its effective date and units, stream a time series, compute spreads and payments, and operationalize production details like caching and business-day gaps — all in PHP, with companion cURL, Python, and JavaScript snippets for cross-language teams.
What SOFR is and how it’s delivered by the API
SOFR is the U.S. dollar secured overnight interbank benchmark. It reflects the cost of overnight borrowing collateralized by U.S. Treasury securities and is published on a daily schedule. In this API, the symbol is SOFR (exactly this identifier). Values are returned as numeric percentage rates (e.g., 3.9 means 3.9% per annum on a simple basis), paired with effective dates so you can apply them correctly to accrual periods.
SOFR in this API is categorized under interbank rates and uses currency USD. It is available as a latest point, a point-in-time historical value, a full daily time series between two dates, change statistics (fluctuation), and OHLC data calculated from the daily series. For benchmarking or loan comparisons, you can also compute an interest cost difference between two symbols.
All endpoints are HTTP GET only. Authentication is a query parameter api_key appended to every request. Base URL is https://interestratesapi.com/api/v1/. Explore more at interestratesapi.com and see the MCP for model and capability pointers.
How to read units, effective dates, and business-day behavior
Rates are returned as decimal percentages (e.g., 3.9 is 3.9%). For interest calculations, convert to a fraction by dividing by 100. The API returns an effective date per symbol — use it to map the rate to the correct accrual window or to forward-fill when weekends and market holidays create gaps. Because SOFR is daily, expect missing entries on non-business days; time series endpoints only include days that have data.
Effective dates matter: if you fetch latest and see dates.SOFR of “2026-09-25,” that’s the value applicable as of that business day’s publication. If your application operates intraday, cache wisely and refresh after the daily publish window relevant to your process. Use HTTP response headers like X-RateLimit-Remaining for pacing and apply your own Redis or application cache keyed by symbol + date to avoid redundant pulls.
Endpoint 1: Catalogue of available symbols
Use /symbols to verify SOFR availability and inspect metadata like frequency and currency. You can filter by base currency and category interbank.
cURL
curl "https://interestratesapi.com/api/v1/symbols?category=interbank&base=USD&api_key=YOUR_API_KEY"
Python (requests)
import requests
resp = requests.get(
"https://interestratesapi.com/api/v1/symbols",
params=dict(category="interbank", base="USD", api_key="YOUR_API_KEY")
)
symbols = resp.json()
print(symbols.get("count"), "USD interbank symbols")
JavaScript (fetch)
const url = "https://interestratesapi.com/api/v1/symbols?category=interbank&base=USD&api_key=YOUR_API_KEY";
const response = await fetch(url);
const data = await response.json();
console.log(data.symbols?.find(s => s.symbol === "SOFR"));
PHP
<?php
$query = http_build_query([
'category' => 'interbank',
'base' => 'USD',
'api_key' => 'YOUR_API_KEY'
]);
$url = "https://interestratesapi.com/api/v1/symbols?$query";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$json = curl_exec($ch);
curl_close($ch);
$data = json_decode($json, true);
$sofr = null;
if (!empty($data['symbols'])) {
foreach ($data['symbols'] as $s) {
if ($s['symbol'] === 'SOFR') { $sofr = $s; break; }
}
}
print_r($sofr);
?
The response includes fields like symbol, name, category, country_code, currency_code, frequency, and description. Locate SOFR and confirm frequency is daily and currency_code is USD. This metadata is useful for building selectors in dashboards and validating inputs to your pricing code.
Endpoint 2: Latest SOFR value
The latest endpoint returns the most recent effective SOFR and its date map. The following JSON uses a real response provided in this guide; use it to confirm your parsing logic end-to-end.
cURL
curl "https://interestratesapi.com/api/v1/latest?symbols=SOFR&api_key=YOUR_API_KEY"
JSON response (real example)
{"success":true,"date":"2026-09-25","base":"USD","rates":{"SOFR":3.9},"dates":{"SOFR":"2026-09-25"},"currencies":{"SOFR":"USD"},"base_filter_note":null}
How to read this:
- rates is a map by symbol to numeric rate (percentage). For SOFR, 3.9 means 3.9%.
- dates map provides the effective date per symbol. Use it for accrual alignment and time series stitching.
- currencies confirms SOFR is USD.
- date at the top-level can reflect the server’s as-of date; rely on dates.SOFR for the per-symbol effective date.
Python (requests)
import requests
r = requests.get(
"https://interestratesapi.com/api/v1/latest",
params=dict(symbols="SOFR", api_key="YOUR_API_KEY")
)
data = r.json()
sofr = data["rates"]["SOFR"]
asof = data["dates"]["SOFR"]
print(f"SOFR {sofr}% as of {asof}")
JavaScript (fetch)
const resp = await fetch("https://interestratesapi.com/api/v1/latest?symbols=SOFR&api_key=YOUR_API_KEY");
const data = await resp.json();
const sofr = data.rates.SOFR;
const asof = data.dates.SOFR;
console.log(`SOFR ${sofr}% as of ${asof}`);
PHP
<?php
$params = http_build_query([
'symbols' => 'SOFR',
'api_key' => 'YOUR_API_KEY'
]);
$url = "https://interestratesapi.com/api/v1/latest?$params";
$raw = file_get_contents($url);
$d = json_decode($raw, true);
$sofr = $d['rates']['SOFR'] ?? null;
$asof = $d['dates']['SOFR'] ?? null;
if ($sofr !== null && $asof) {
echo "SOFR {$sofr}% as of {$asof}\n";
}
?
Operational notes:
- Cache the entire JSON for a short TTL and key on the date to keep history immutable.
- Weekend/holiday gaps: the latest effective date might be the prior business day.
Endpoint 3: Historical SOFR on a specific date
Use /historical when you need SOFR as of a given day, e.g., to price backdated loans or reconcile accruals. For monthly symbols, the service returns the last day with data in that month; SOFR is daily, so you’ll get the exact business day available.
cURL
curl "https://interestratesapi.com/api/v1/historical?date=2025-06-15&symbols=SOFR&api_key=YOUR_API_KEY"
Python (requests)
import requests
resp = requests.get(
"https://interestratesapi.com/api/v1/historical",
params=dict(date="2025-06-15", symbols="SOFR", api_key="YOUR_API_KEY")
)
data = resp.json()
# Values below are illustrative per the API docs when not using the real response above
print(data)
JavaScript (fetch)
const u = "https://interestratesapi.com/api/v1/historical?date=2025-06-15&symbols=SOFR&api_key=YOUR_API_KEY";
const res = await fetch(u);
const hist = await res.json();
console.log(hist.rates?.SOFR, hist.base);
PHP
<?php
$params = http_build_query([
'date' => '2025-06-15',
'symbols' => 'SOFR',
'api_key' => 'YOUR_API_KEY'
]);
$url = "https://interestratesapi.com/api/v1/historical?$params";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$raw = curl_exec($ch);
curl_close($ch);
$d = json_decode($raw, true);
$val = $d['rates']['SOFR'] ?? null;
$ccy = $d['base'] ?? $d['currencies']['SOFR'] ?? 'USD';
if ($val !== null) {
echo "Historical SOFR: {$val}% in {$ccy}\n";
}
?
Illustrative JSON
{
"success": true,
"date": "2025-06-15",
"base": "USD",
"rates": { "SOFR": 5.33 },
"currencies": { "SOFR": "USD" }
}
Use cases:
- Loan resets: get the fixing applicable to a backdated reset date.
- Audit: reconstruct daily curves for end-of-day reporting.
Endpoint 4: Daily SOFR time series
Fetch a bounded series to drive charts, moving averages, or volatility estimates. Because SOFR is daily, the rates map returns per-day entries for business days within the window.
cURL
curl "https://interestratesapi.com/api/v1/timeseries?start=2025-09-29&end=2026-09-29&symbols=SOFR&api_key=YOUR_API_KEY"
Python (requests)
import requests
import statistics as stats
resp = requests.get(
"https://interestratesapi.com/api/v1/timeseries",
params=dict(start="2025-09-29", end="2026-09-29", symbols="SOFR", api_key="YOUR_API_KEY")
)
ts = resp.json()
points = ts["rates"]["SOFR"]
values = list(points.values())
if values:
print("Average SOFR:", sum(values)/len(values))
JavaScript (fetch)
const r = await fetch("https://interestratesapi.com/api/v1/timeseries?start=2025-09-29&end=2026-09-29&symbols=SOFR&api_key=YOUR_API_KEY");
const ts = await r.json();
const series = ts.rates.SOFR; // {"2025-01-02": 5.33, ...}
const dates = Object.keys(series).sort();
console.log("First point:", dates[0], series[dates[0]]);
PHP
<?php
$params = http_build_query([
'start' => '2025-09-29',
'end' => '2026-09-29',
'symbols' => 'SOFR',
'api_key' => 'YOUR_API_KEY'
]);
$url = "https://interestratesapi.com/api/v1/timeseries?$params";
$raw = file_get_contents($url);
$d = json_decode($raw, true);
$series = $d['rates']['SOFR'] ?? [];
ksort($series);
foreach ($series as $date => $val) {
echo "$date,$val\n";
}
?
Illustrative JSON
{
"success": true,
"base": "USD",
"start_date": "2025-09-29",
"end_date": "2026-09-29",
"rates": {
"SOFR": {
"2025-01-02": 5.33,
"2025-01-03": 5.33,
"2025-01-06": 5.33
}
},
"frequencies": { "SOFR": "daily" },
"currencies": { "SOFR": "USD" }
}
Notes:
- Keys in the series are YYYY-MM-DD (UTC calendar dates). Missing weekends/holidays are simply absent.
- Use frequencies.SOFR to confirm daily granularity when writing symbol-agnostic analytics.
Endpoint 5: Fluctuation (change statistics)
Use /fluctuation to compute deltas over a date range without transferring the entire time series. It returns start/end values, change, percentage change, and the high/low across the window.
cURL
curl "https://interestratesapi.com/api/v1/fluctuation?start=2025-09-29&end=2026-09-29&symbols=SOFR&api_key=YOUR_API_KEY"
Python (requests)
import requests
f = requests.get(
"https://interestratesapi.com/api/v1/fluctuation",
params=dict(start="2025-09-29", end="2026-09-29", symbols="SOFR", api_key="YOUR_API_KEY")
).json()
stats = f["rates"]["SOFR"]
print("Change (bps):", (stats["change"])*100)
JavaScript (fetch)
const resp = await fetch(
"https://interestratesapi.com/api/v1/fluctuation?start=2025-09-29&end=2026-09-29&symbols=SOFR&api_key=YOUR_API_KEY"
);
const out = await resp.json();
const s = out.rates.SOFR;
console.log(`From ${s.start_date}=${s.start_value}% to ${s.end_date}=${s.end_value}% change=${s.change}% (${s.change_pct}%)`);
PHP
<?php
$params = http_build_query([
'start' => '2025-09-29',
'end' => '2026-09-29',
'symbols' => 'SOFR',
'api_key' => 'YOUR_API_KEY'
]);
$url = "https://interestratesapi.com/api/v1/fluctuation?$params";
$raw = file_get_contents($url);
$d = json_decode($raw, true);
$s = $d['rates']['SOFR'] ?? null;
if ($s) {
echo "High: {$s['high']}%, Low: {$s['low']}%, Change: {$s['change']}% ({$s['change_pct']}%)\n";
}
?
Illustrative JSON
{
"success": true,
"rates": {
"SOFR": {
"start_date": "2025-09-29",
"end_date": "2026-09-29",
"start_value": 5.50,
"end_value": 5.33,
"change": -0.17,
"change_pct": -3.09,
"high": 5.50,
"low": 5.25
}
}
}
Key fields: change is absolute percentage points; change_pct is relative percentage change. Use high/low for drawdown and range analysis in risk metrics.
Endpoint 6: OHLC (open-high-low-close) for SOFR
OHLC is aggregated from the daily series on the fly. Use it for candlestick charts or for quickly summarizing monthly or weekly moves. data_points indicates how many business-day observations contributed to the period.
cURL
curl "https://interestratesapi.com/api/v1/ohlc?symbols=SOFR&period=monthly&start=2025-09-29&end=2026-09-29&api_key=YOUR_API_KEY"
Python (requests)
import requests
o = requests.get(
"https://interestratesapi.com/api/v1/ohlc",
params=dict(symbols="SOFR", period="monthly", start="2025-09-29", end="2026-09-29", api_key="YOUR_API_KEY")
).json()
rows = o["rates"]["SOFR"]
for row in rows:
print(row["period"], row["open"], row["high"], row["low"], row["close"], row["data_points"])
JavaScript (fetch)
const url = "https://interestratesapi.com/api/v1/ohlc?symbols=SOFR&period=monthly&start=2025-09-29&end=2026-09-29&api_key=YOUR_API_KEY";
const res = await fetch(url);
const ohlc = await res.json();
console.log(ohlc.rates.SOFR.map(p => `${p.period}: O=${p.open} H=${p.high} L=${p.low} C=${p.close} N=${p.data_points}`));
PHP
<?php
$params = http_build_query([
'symbols' => 'SOFR',
'period' => 'monthly',
'start' => '2025-09-29',
'end' => '2026-09-29',
'api_key' => 'YOUR_API_KEY'
]);
$url = "https://interestratesapi.com/api/v1/ohlc?$params";
$raw = file_get_contents($url);
$d = json_decode($raw, true);
foreach ($d['rates']['SOFR'] as $row) {
echo "{$row['period']},{$row['open']},{$row['high']},{$row['low']},{$row['close']},{$row['data_points']}\n";
}
?
Illustrative JSON
{
"success": true,
"period": "monthly",
"start_date": "2025-09-29",
"end_date": "2026-09-29",
"rates": {
"SOFR": [
{
"period": "2025-01",
"open": 5.50,
"high": 5.50,
"low": 5.33,
"close": 5.33,
"data_points": 23
}
]
}
}
Endpoint 7: Convert (loan interest cost comparison)
/convert approximates the total simple interest for a loan at the latest rate of each of two symbols, then returns the difference. Use this to quantify a spread’s effect on total interest for borrowing amounts and terms.
cURL
curl "https://interestratesapi.com/api/v1/convert?from=SOFR&to=ECB_MRO&amount=100000&term_months=12&api_key=YOUR_API_KEY"
Python (requests)
import requests
cmp_ = requests.get(
"https://interestratesapi.com/api/v1/convert",
params=dict(from="SOFR", to="ECB_MRO", amount=100000, term_months=12, api_key="YOUR_API_KEY")
).json()
print("Rate spread:", cmp_["difference"]["rate_spread"])
print("Interest saved:", cmp_["difference"]["interest_saved"])
JavaScript (fetch)
const c = await fetch("https://interestratesapi.com/api/v1/convert?from=SOFR&to=ECB_MRO&amount=100000&term_months=12&api_key=YOUR_API_KEY");
const diff = await c.json();
console.log(diff.difference);
PHP
<?php
$params = http_build_query([
'from' => 'SOFR',
'to' => 'ECB_MRO',
'amount' => 100000,
'term_months' => 12,
'api_key' => 'YOUR_API_KEY'
]);
$url = "https://interestratesapi.com/api/v1/convert?$params";
$raw = file_get_contents($url);
$d = json_decode($raw, true);
$spread = $d['difference']['rate_spread'] ?? null;
$saved = $d['difference']['interest_saved'] ?? null;
echo "Spread: {$spread}; Interest saved: {$saved}\n";
?
Illustrative JSON
{
"success": true,
"amount": 100000,
"term_months": 12,
"from": {
"symbol": "SOFR",
"rate": 5.33,
"date": "2026-09-29",
"total_interest": 5330.00,
"total_payment": 105330.00
},
"to": {
"symbol": "ECB_MRO",
"rate": 4.50,
"date": "2026-09-29",
"total_interest": 4500.00,
"total_payment": 104500.00
},
"difference": {
"rate_spread": 0.83,
"interest_saved": 830.00
}
}
How to compute spreads and payments from returned values
If you need to compute your own spread or loan payment outside of /convert:
- Rate spread in percentage points: spread_pp = rate_A - rate_B (both as percentages). For example, if SOFR is r_sofr and another benchmark r_x, then spread_pp = r_sofr - r_x. Convert to basis points with spread_bps = spread_pp * 100.
- Monthly payment for an amortizing loan at an annual rate r% with principal P and term n months:
- Convert to monthly fraction i = (r/100)/12.
- Payment M = P * i / (1 - (1 + i)^(-n))
- Simple-interest estimate for short terms: total_interest ≈ P * (r/100) * (days/365). For month-level approximations, days ≈ 30. Always align the rate’s effective date to your accrual window using dates.SOFR.
If you use latest with multiple symbols, the response includes per-symbol dates to ensure you only compare rates that are as-of the same calendar day. If not aligned, you can choose to backfill one symbol to match the other’s date using /historical.
Caching, latency, and production concerns
- All requests are HTTP GET and authenticated via api_key query parameter. No headers are required for auth.
- Apply client-side caching keyed by (endpoint, symbol set, date parameters). For latest SOFR, a short TTL until the next business-day publish window is typical.
- Business days only: daily SOFR does not appear on weekends/holidays. Handle gaps in time series logic and display (e.g., axis uses business-day ticks).
- Backfill strategy: when needing same-day alignment across multiple symbols, use each symbol’s date map; if misaligned, fetch /historical for the lagging symbol’s date.
- Rate limit headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset help you schedule retries and pacing. On HTTP 429, read Retry-After.
- Error handling: the common error shape is success=false with an error string (and details in some 404s). Handle 401 for invalid/missing keys, 403 for inactive account, 422 for validation errors like bad date formats, and 404 for out-of-range dates.
End-to-end PHP: building a minimal SOFR client
The following PHP function fetches latest SOFR, confirms currency and date, then returns a typed array for downstream use:
<?php
function get_latest_sofr(string $apiKey): array {
$url = "https://interestratesapi.com/api/v1/latest?symbols=SOFR&api_key=" . urlencode($apiKey);
$raw = file_get_contents($url);
if ($raw === false) {
throw new RuntimeException("Network error fetching SOFR");
}
$d = json_decode($raw, true);
if (!($d['success'] ?? false)) {
$msg = $d['error'] ?? 'Unknown error';
throw new RuntimeException("API error: $msg");
}
$value = $d['rates']['SOFR'] ?? null;
$asof = $d['dates']['SOFR'] ?? null;
$ccy = $d['currencies']['SOFR'] ?? null;
if ($value === null || $asof === null || $ccy !== 'USD') {
throw new RuntimeException("Malformed or unexpected latest response");
}
return ['value_pct' => $value, 'asof' => $asof, 'currency' => $ccy];
}
// Example usage:
try {
$sofr = get_latest_sofr('YOUR_API_KEY');
echo "SOFR {$sofr['value_pct']}% as of {$sofr['asof']} ({$sofr['currency']})\n";
} catch (Throwable $e) {
error_log($e->getMessage());
}
?
To add historical windows, layer in a helper for /timeseries and compute summary stats (e.g., min, max, average), then store results in your application cache keyed by start/end dates to avoid recomputation.
Real-world use cases with SOFR in PHP
- Interest rate dashboards: pull /latest and /timeseries for SOFR, compute moving averages, and render charts. Use OHLC for quick monthly movement summaries.
- Loan and mortgage platforms: gather latest SOFR, compute rate spreads vs. internal margins, and estimate simple-interest or amortized payments. Use /convert for a fast interest cost comparison to an alternative benchmark.
- Treasury and ALM tooling: feed SOFR time series into risk models and daily P&L. Use /fluctuation to track month-to-date or quarter-to-date moves without downloading entire series.
- Macro research: rely on /historical to reconstruct point-in-time analyses, ensuring you apply the rate using dates.SOFR for accuracy.
Explore the full API surface at interestratesapi.com and see model capabilities in the MCP.
Putting it together: end-to-end checklist
- Authentication: append ?api_key=YOUR_API_KEY to every GET URL.
- Symbol: use SOFR exactly as written (case-sensitive where relevant in your code).
- Units: values are percentages; convert to fractions by dividing by 100 for math.
- Effective date: read dates.SOFR to map to accrual windows.
- Business days: handle weekend/holiday gaps; use /timeseries to avoid synthetic fills.
- Caching: cache latest by symbol and as-of date; cache series by (symbol, start, end).
- Errors and rate limits: parse JSON error shape; respect Retry-After on 429.
FAQ
Q: Are SOFR values returned as decimals (0.039) or percentages (3.9)?
A: Percentages. For calculations, divide by 100 to get the rate fraction (e.g., 3.9 becomes 0.039).
Q: How do I handle days with no SOFR?
A: The API only returns business days with data. On weekends/holidays, latest keeps the last available effective date. For aligned comparisons, fetch /historical for the date you need.
Q: Can I authenticate using headers?
A: No. All endpoints are GET and use the api_key query parameter exclusively.
Q: How do I compute a monthly loan payment using SOFR?
A: Convert the SOFR percentage to a monthly decimal rate i = (SOFR/100)/12, then use M = P * i / (1 - (1 + i)^(-n)). Align the rate using dates.SOFR to your billing cycle.
Q: What happens if I request an out-of-range date?
A: You’ll receive success=false with a 404 and may see a details field indicating available ranges. Handle this by adjusting dates or querying /timeseries to inspect coverage.
Build your SOFR integration now. Get your API key and ship with confidence: Register. For a broader view of models and capabilities, visit the MCP. If you prefer to browse first, head to interestratesapi.com.




