GuidesFINANCE APIS
FRED API from JavaScript
The FRED API at api.stlouisfed.org sent no CORS headers in our 28 October 2026 tests, not even on its error responses, so fetch() from a web page is blocked. The free https://proxy.cors.dev/ tier (no signup, GET and HEAD, about 600 requests a minute per IP) forwards the api_key query parameter unchanged, and through it the keyless fredgraph.csv download returned 14 KB of unemployment data from 1948 to September 2026. The key stays visible in the request URL: fine for apps where each user brings their own key, not for a key you pay for.
Why the FRED API fails in the browser
FRED API version 1 lives at https://api.stlouisfed.org/fred/. Every request needs api_key, a 32-character lower-case alphanumeric string, in the query string, and file_type=json if you want JSON instead of the default XML.
On 28 October 2026 no response from api.stlouisfed.org carried a CORS header. An unregistered key returned 400 with {"error_code":400,"error_message":"Bad Request. The value for variable api_key is not registered. ..."}, a missing key returned 400 "Variable api_key is not set.", and the OPTIONS preflight got 400 too. The browser blocks the response, so your code sees a CORS error instead of FRED's message; a Stack Overflow question from 2014 reports the same error.
Through https://proxy.cors.dev/ the query string is forwarded unchanged, so api_key reaches FRED: with our unregistered test key, FRED's own 400 JSON came back with Access-Control-Allow-Origin and the page could read it. We had no registered key, so the successful response below is described from FRED's documentation, not from our test.
| Endpoint | Auth | Browser CORS | cors.dev free | Tested response |
|---|---|---|---|---|
| fred/series/observations | api_key in query | No | Forwards the key | 400 JSON, unregistered key |
| fred/series | api_key in query | No | Forwards the key | 400 JSON, unregistered key |
| fred/v2/release/observations | Authorization: Bearer | No | 400 header_not_allowed | 401 JSON without a key |
| graph/fredgraph.csv?id=UNRATE | None | No | 200 | 14 KB CSV, 1948 to Sep 2026 |
| fredgraph.csv?id=UNRATE,CPIAUCSL | None | No | 200 | Two series, one column each |
| fredgraph.csv?id=GDP with cosd, coed | None | No | 200 | 567 bytes, quarterly |
| graph/fredgraph.xlsx?id=UNRATE | None | No | 200 | 19 KB Excel file |
| fredgraph.csv with an unknown id | None | No | 404 | HTML error page |
Get a FRED API key
- Sign in
create or log in to an account at
fredaccount.stlouisfed.org. Keys can only be requested or viewed while you are logged in. - Request one key per app
FRED's key page says "Developers should request a distinct API key for each application they build."
- Decide who supplies the key
the same page says "All users of an application shall use their own API key."
A key in browser code is visible to every visitor, right in the request URL in the network panel, and cors.dev does not hide it. FRED's own rule points to a bring-your-own-key design for browser apps: each user pastes their key, and your page sends it with their requests.
Series observations through cors.dev
fred/series/observations returns the data points of one series. The parameters you will use most:
series_id: required, such asUNRATE,GDPorCPIAUCSLobservation_startandobservation_end:YYYY-MM-DDunits:lin,chg,ch1,pch,pc1,pca,cch,ccaorlogfrequencyandaggregation_method: such asm,qora, combined withavg,sumoreoplimit(1 to 100000, default 100000),sort_order(ascordesc) andfile_type(xml,json,xlsx, orcsv, which arrives zipped)
The JSON has an observations array of objects with date and value. Values are strings, so convert them with Number() and drop entries that are not numbers:
// Runs in a module script or the DevTools console
const proxy = 'https://proxy.cors.dev/';
// The key travels in the URL, so every visitor can read it
const apiKey = 'your32characterfredapikeyhere000';
async function observations(seriesId, options = {}) {
const params = new URLSearchParams({ series_id: seriesId, api_key: apiKey, file_type: 'json', ...options });
const response = await fetch(proxy + 'https://api.stlouisfed.org/fred/series/observations?' + params);
const data = await response.json();
if (!response.ok) throw new Error(data.error_message ?? data.message ?? `HTTP ${response.status}`);
return data.observations
.map((row) => ({ date: row.date, value: Number(row.value) }))
.filter((row) => Number.isFinite(row.value));
}
console.table(await observations('UNRATE', { observation_start: '2026-01-01' }));API version 2 adds fred/v2/release/observations, which returns every series on a release in one request. It takes the key in an Authorization: Bearer header: without one it returned 401 {"code":401,"message":"Missing or invalid credentials."} with no CORS header. The free proxy refuses Authorization with 400 and X-Cors-Error: header_not_allowed; cors.dev Pro forwards it.
CSV downloads without a key
https://fred.stlouisfed.org/graph/fredgraph.csv?id=UNRATE is the download behind FRED's graphs. It is not part of the documented API, so the URL can change, and it needs no key and sends no CORS header.
Through cors.dev it returned 200 with 14 KB of CSV: header observation_date,UNRATE, monthly rows from 1948-01-01 to 2026-09-01, last value 4.2.
id: one series, or a comma list for one column per seriescosdandcoed: start and end date;id=GDP&cosd=2020-01-01&coed=2026-10-01returned 567 bytes, last row 2026-04-01 with 32563.030- For several series, one
cosdper series:id=UNRATE,CPIAUCSL&cosd=2024-01-01,2024-01-01
// Runs in a module script or the DevTools console
const proxy = 'https://proxy.cors.dev/';
async function fredCsv(seriesId, startDate) {
const params = new URLSearchParams({ id: seriesId });
if (startDate) params.set('cosd', startDate);
const response = await fetch(proxy + 'https://fred.stlouisfed.org/graph/fredgraph.csv?' + params);
if (!response.ok) throw new Error(`FRED CSV failed: ${response.headers.get('X-Cors-Error') ?? response.status}`);
// The first line is observation_date,<series id>; days without data have an empty value
const [, ...lines] = (await response.text()).trim().split('\n');
return lines
.map((line) => line.split(','))
.filter(([, value]) => value !== '')
.map(([date, value]) => ({ date, value: Number(value) }));
}
const unemployment = await fredCsv('UNRATE', '2025-01-01');
console.log(unemployment.at(-1));Daily series leave the value empty on days without data, as DGS10 did on 2026-10-12. An unknown id returns 404 with an HTML page, and fredgraph.xlsx?id=UNRATE returns a 19 KB Excel file instead of CSV.
Rate limits, terms and the right cors.dev tier
- Rate limit: up to 120 requests per minute per key before FRED answers
429, and ignoring the throttling can lead to a temporary block. Other documented status codes are400,404,423and500. - Notice: your app must show "This product uses the FRED® API but is not endorsed or certified by the Federal Reserve Bank of St. Louis."
- Hostname: "FRED", "ALFRED" and "Federal Reserve Bank" may not appear in it, and FRED may impose or change limits at any time.
- cors.dev free: prefix the URL with
https://proxy.cors.dev/. No signup, GET and HEAD only, about 600 requests a minute per IP, no daily quota, responses up to 6 MiB, no caching. Query-string keys are forwarded;Authorizationis refused. - cors.dev Pro: $5 a month for 500,000 requests, with a 7-day trial of 1,000 requests and no card. It forwards
Authorizationfor v2, allows 3,000 requests a minute per account and 100 in flight, and adds an optional GET cache withX-Cors-Cache: 60(1 to 300 seconds; cache hits still count). FRED v1 URLs carryapi_keyandfredgraph.csvsets cookies, so neither is cached (X-Cors-Cache-Status: BYPASS).
cors.dev Pro fits bring-your-own-key apps, internal tools and prototypes. If your app sends your own key for every visitor, keep that call on your backend or a serverless function.
Good questions.
Why does the FRED API give a CORS error in the browser?
api.stlouisfed.org sends no Access-Control-Allow-Origin header, not even on its own error responses or the OPTIONS preflight. The browser therefore hides every response from your code. Send the request through a CORS proxy or from a server.
Does a CORS proxy hide my FRED API key?
No. FRED version 1 takes api_key in the query string, so the key is in the request URL that every visitor can see in the network panel. To use your own key for all visitors without exposing it, call FRED from your backend.
Can I get FRED data without an API key?
Yes, from https://fred.stlouisfed.org/graph/fredgraph.csv?id=UNRATE, the CSV download behind FRED's graphs. It sends no CORS header, but through cors.dev it returned 14 KB of monthly unemployment data from 1948 to September 2026. It is not part of the documented API.
How many requests can I make to the FRED API?
FRED allows up to 120 requests per minute per key before it answers 429, and ignoring the throttling can lead to a temporary block. FRED also reserves the right to change limits at any time.
How do I load several FRED series in one request?
With fredgraph.csv, pass a comma list in id and one start date per series in cosd, such as id=UNRATE,CPIAUCSL&cosd=2024-01-01,2024-01-01, for one column per series. With the API, fred/v2/release/observations returns every series on a release but needs an Authorization: Bearer header.
Do I have to credit FRED in my app?
Yes. FRED's terms require the notice "This product uses the FRED® API but is not endorsed or certified by the Federal Reserve Bank of St. Louis." on your application, and they forbid "FRED", "ALFRED" or "Federal Reserve Bank" in your hostname.