GuidesFINANCE APIS

Free economic data APIs for JavaScript

Eight of the ten free economic data APIs we tested on 28 October 2026 send CORS headers, so browser JavaScript can call them directly: BLS, World Bank, Eurostat, the ECB, OECD, BEA, Census and US Treasury Fiscal Data. FRED and the IMF DataMapper send none; through the free https://proxy.cors.dev/ tier (no signup, GET and HEAD, about 600 requests a minute per IP) the IMF file and the keyless FRED CSV both returned 200. Census, BEA and FRED need a free key, and a key in page code is visible to every visitor.

Ten free economic data APIs tested from the browser

On 28 October 2026 we called each API with an Origin header of https://example.com, directly and through https://proxy.cors.dev/. "Sends CORS" means the response carried Access-Control-Allow-Origin, so page JavaScript can read it without a proxy.

Free economic data APIs from the browser, checked October 2026
APIKeyLimitsSends CORSThrough cors.dev
FREDFree, in the URL120 requests a minuteNoReadable, key forwarded
IMF DataMapperNoneNone publishedNo200, 122 KB
BLSv1 none, v2 freev1 25 a day, v2 500 a dayYesShared quota used up
World BankNoneNone publishedYes200, not needed
EurostatNoneNone publishedYes200, not needed
ECB Data PortalNoneNone publishedYes200, not needed
OECDNone60 downloads an hourYes200, not needed
BEAFree UserID100 requests a minuteYesNot needed
CensusFree, required50 variables a queryYesNot needed
Treasury Fiscal DataNoneNone publishedYes504 timeout

"None published" means we found no request limit in the provider's API documentation.

Call these directly: no proxy needed

The US Bureau of Labor Statistics (api.bls.gov) sends Access-Control-Allow-Origin: *, so you call it directly. Version 1 needs no key: 25 queries a day, 25 series and 10 years per query.

Version 2 needs a free registration, renewed yearly: 500 queries a day, 50 series and 20 years. Both allow 50 requests per 10 seconds.

Several series at once means a POST. A JSON body triggers a preflight, and BLS answered that OPTIONS request with 405. A form-encoded body built with URLSearchParams is a simple request with no preflight, and it worked: CPI-U (CUUR0000SA0) for September 2026 was 336.940 and the unemployment rate (LNS14000000) 4.2.

CPI and unemployment from BLS, no proxy
// Runs in a module script or the DevTools console
// BLS sends CORS headers, so no proxy. A form body keeps the POST a simple request:
// BLS answers the preflight of a JSON POST with 405
const response = await fetch('https://api.bls.gov/publicAPI/v2/timeseries/data/', {
  method: 'POST',
  body: new URLSearchParams({ seriesid: 'CUUR0000SA0,LNS14000000', startyear: '2025', endyear: '2026' }),
});
const data = await response.json();
if (data.status !== 'REQUEST_SUCCEEDED') throw new Error(data.message.join(' '));

// Newest observation first for each series
const latest = data.Results.series.map(({ seriesID, data: rows }) => ({
  series: seriesID,
  period: `${rows[0].periodName} ${rows[0].year}`,
  value: Number(rows[0].value),
}));
console.table(latest);

Keep BLS off the proxy: through cors.dev it answered REQUEST_NOT_PROCESSED, because the keyless daily quota for requests coming from the proxy was already used up. The other direct options:

  • World Bank (api.worldbank.org/v2, add format=json): no key; the docs say "API keys and other authentication methods are no longer necessary".
  • Eurostat (ec.europa.eu/eurostat/api/dissemination/statistics/1.0/data/une_rt_m): JSON-stat, no key.
  • ECB Data Portal (data-api.ecb.europa.eu/service/data/EXR/D.USD.EUR.SP00.A): no key; format=csvdata for CSV.
  • OECD (sdmx.oecd.org/public/rest/data/..., format=csv): no key, echoes your origin. The limit is 60 data downloads per hour, and OECD does not allow traffic from VPNs or anonymised sources, so call it directly. Include the agency: OECD.SDD.STES,DSD_STES@DF_CLI,4.1 returned data, and so did an empty version after the comma, while DSD_STES@DF_CLI,4.1 without the agency returned 404.
  • BEA (apps.bea.gov/api/data): free registration for a 36-character UserID in the query string. Limits are 100 requests, 100 MB and 30 errors per minute; past that, requests fail with 429 for a timeout, currently one minute.
  • Census (api.census.gov/data/...): a key is required on every data query, up to 50 variables per query. Without one it answers 302 with no CORS header and redirects to a "Missing Key" HTML page, so a browser fetch fails with a CORS error.
  • US Treasury Fiscal Data (api.fiscaldata.treasury.gov): no key and no account. It answered directly, but through cors.dev it timed out with 504 upstream_timeout on 3 of 3 attempts, so call it directly.

FRED and the IMF need a proxy

FRED (api.stlouisfed.org) needs a free key as api_key in the query string, allows up to 120 requests per minute and sends no CORS header, not even on errors. Through https://proxy.cors.dev/ the key is forwarded and the JSON becomes readable. The keyless fred.stlouisfed.org/graph/fredgraph.csv?id=UNRATE returned 14 KB through cors.dev; the FRED API guide covers both in detail.

The IMF DataMapper (www.imf.org/external/datamapper/api/v1/NGDP_RPCH) needs no key and sends no CORS header; through cors.dev it returned 200 with 122 KB of JSON. The docs say you can append country codes and a periods parameter, but v1 and v2 ignored both in our tests and always returned all 229 countries, so filter in your code.

IMF DataMapper through cors.dev
// Runs in a module script or the DevTools console
const proxy = 'https://proxy.cors.dev/';

// DataMapper sends no CORS header and returned every country in our tests, so we filter here
async function imfIndicator(indicator, countries, years) {
  const response = await fetch(proxy + 'https://www.imf.org/external/datamapper/api/v1/' + indicator);
  if (!response.ok) throw new Error(`IMF failed: ${response.headers.get('X-Cors-Error') ?? response.status}`);

  const series = (await response.json()).values[indicator];
  return countries.flatMap((country) =>
    years.map((year) => ({ country, year, value: series[country]?.[year] ?? null })),
  );
}

console.table(await imfIndicator('NGDP_RPCH', ['USA', 'CHN', 'DEU', 'IND'], ['2025', '2026']));

The example printed IMF real GDP growth for 2025 and 2026: USA 2.3 and 2.3, China 5 and 4.5, Germany 0.2 and 1.3, India 7.8 and 7.

API keys in browser code

A key in a request URL or in page code is visible to every visitor in the network panel, whether you call the API directly or through cors.dev, which does not hide keys. Census and BEA send CORS, so they work from the browser with a key, but anyone can copy it.

FRED requires each user of an application to use their own key, so a field where users paste their own key suits dashboards and student projects. For a key you do not want exposed, call the API from your backend.

Limits and which cors.dev tier fits

  • 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. Enough for FRED and IMF data in a dashboard or prototype.
  • cors.dev Pro: $5 a month for 500,000 requests, with a 7-day trial of 1,000 requests and no card. 3,000 requests a minute per account and 100 in flight, and an optional GET cache with X-Cors-Cache: 60 (1 to 300 seconds; cache hits still count as requests). The IMF file and FRED are not cached: imf.org and fredgraph.csv set cookies, and FRED API URLs carry api_key. Start the trial.

Good questions.

Which free economic data APIs work from JavaScript without a proxy?

In our 28 October 2026 test, BLS, World Bank, Eurostat, the ECB Data Portal, OECD, BEA, Census and US Treasury Fiscal Data all sent Access-Control-Allow-Origin. FRED and the IMF DataMapper did not, so a web page needs a proxy or a backend for those two.

Why does my BLS POST request fail with 405 in the browser?

A POST with a JSON body and Content-Type: application/json triggers a preflight OPTIONS request, which api.bls.gov answered with 405. Send the body as URLSearchParams instead: a form-encoded POST is a simple request, so there is no preflight.

Do I need an API key for BLS data?

No, version 1 works without one: 25 queries a day, 25 series and 10 years per query. A free version 2 registration raises that to 500 queries a day, 50 series and 20 years, and has to be renewed once a year.

Why does the Census API return HTML instead of JSON?

Census requires a key on every data query. Without one, api.census.gov answers 302 and redirects to its "Missing Key" HTML page. The redirect has no CORS header, so in the browser fetch fails with a CORS error; through a proxy or on a server you get the HTML page with status 200, and response.json() throws. With a key, the JSON came back with Access-Control-Allow-Origin: *.

Why does my OECD API query return a 404 error?

The data URL needs the agency, the dataflow and a version that exists. OECD.SDD.STES,DSD_STES@DF_CLI,4.1 returned data in our test, and so did the same query with the version left empty. Without the agency, DSD_STES@DF_CLI,4.1 returned 404, and a wrong version such as 4.0 returned 404 with NoRecordsFound.

Can I get FRED data without an API key?

Yes, through the CSV download behind FRED's graphs: fred.stlouisfed.org/graph/fredgraph.csv?id=UNRATE needs no key and returned 14 KB through cors.dev. The JSON API at api.stlouisfed.org always needs api_key.