# Commodity price history Source: https://developer.hayinsights.com/api-reference/commodities/commodity-price-history /openapi.yaml get /openapi/v1/commodities/{id}/history Historical closing prices for a single commodity. Use the `id` slug returned by the list endpoint (e.g. `gold`, `wti-crude`). # Featured commodities Source: https://developer.hayinsights.com/api-reference/commodities/featured-commodities /openapi.yaml get /openapi/v1/commodities/featured A curated set of headline commodities (gold, crude oil, copper, …) with the latest close, 1-day change and a short price sparkline. # Gold vs DXY correlation Source: https://developer.hayinsights.com/api-reference/commodities/gold-vs-dxy-correlation /openapi.yaml get /openapi/v1/commodities/gold-dxy Rebased gold price and US Dollar Index (DXY) series over the requested window, for visualising their (typically inverse) correlation. # List commodity prices Source: https://developer.hayinsights.com/api-reference/commodities/list-commodity-prices /openapi.yaml get /openapi/v1/commodities Latest price snapshot for every tracked commodity across energy, metals, agriculture and macro categories — including 1-day / MTD / YTD changes, the 50-period moving average, a trend classification and a sparkline. # Top movers Source: https://developer.hayinsights.com/api-reference/commodities/top-movers /openapi.yaml get /openapi/v1/commodities/movers The biggest gaining and losing commodities over the requested period. # Crypto bubble map Source: https://developer.hayinsights.com/api-reference/crypto/crypto-bubble-map /openapi.yaml get /openapi/v1/crypto/bubble-map Top coins by market capitalisation with their price change over a selected timeframe and a normalised bubble size, for a bubble-map visualisation. # Crypto derivatives metrics Source: https://developer.hayinsights.com/api-reference/crypto/crypto-derivatives-metrics /openapi.yaml get /openapi/v1/crypto/derivatives Derivatives-market metrics — open interest, funding rates, 24h liquidations and the options put/call ratio for the major coins. # Crypto market dominance Source: https://developer.hayinsights.com/api-reference/crypto/crypto-market-dominance /openapi.yaml get /openapi/v1/crypto/dominance Market-cap dominance breakdown — total market cap and each major coin's share of it, with the freshness of the underlying source data. # Crypto market overview Source: https://developer.hayinsights.com/api-reference/crypto/crypto-market-overview /openapi.yaml get /openapi/v1/crypto Snapshot of the crypto market — total market capitalisation, 24h volume, major-coin dominance split, the Fear & Greed index and trending coins. # Crypto on-chain metrics Source: https://developer.hayinsights.com/api-reference/crypto/crypto-on-chain-metrics /openapi.yaml get /openapi/v1/crypto/onchain On-chain network metrics — Ethereum gas prices, Bitcoin hashrate and active addresses, whale transaction count and the MVRV / NUPL valuation indicators. # Compare fund composition Source: https://developer.hayinsights.com/api-reference/etf/compare-fund-composition /openapi.yaml get /openapi/v1/etf/compare/fund-composition Side-by-side composition for two to five Vietnamese funds — asset allocation, the equity portfolio and the sector breakdown — for direct comparison. Pass the VN fund codes (e.g. `VESAF`) as a comma-separated `codes` list. # Compare NAV growth Source: https://developer.hayinsights.com/api-reference/etf/compare-nav-growth /openapi.yaml get /openapi/v1/etf/compare/nav-growth NAV-growth series for two Vietnamese funds, benchmarked against the VN-Index, across the available period windows. Pass the two VN fund codes (e.g. `VESAF`) as `code1` and `code2`. # ETF detail Source: https://developer.hayinsights.com/api-reference/etf/etf-detail /openapi.yaml get /openapi/v1/etf/{code}/detail Full detail view for a single ETF — descriptive metadata, latest price, top holdings, valuation ratios, sector weights, net-flow history and cumulative-return performance by period. Use the exchange ticker (e.g. a TSE code like `1306`) as the `{code}` path param. # ETF / fund composition Source: https://developer.hayinsights.com/api-reference/etf/etf-fund-composition /openapi.yaml get /openapi/v1/etf/{code}/composition Full composition of a single Vietnamese fund certificate — asset allocation, the equity portfolio and the sector breakdown. Use the VN fund code (e.g. `VESAF`) as the `{code}` path param. # ETF holdings Source: https://developer.hayinsights.com/api-reference/etf/etf-holdings /openapi.yaml get /openapi/v1/etf/{code}/holdings The underlying constituent holdings of an ETF's basket — ticker, name, shares held and position value, as of the latest fund snapshot. Use the exchange ticker (e.g. a TSE code like `1306`) as the `{code}` path param. # ETF info Source: https://developer.hayinsights.com/api-reference/etf/etf-info /openapi.yaml get /openapi/v1/etf/{code}/info Core descriptive metadata for a single ETF — manager, benchmark index, listing date, trust fee and distribution yield. Use the exchange ticker (e.g. a TSE code like `1306`) as the `{code}` path param. # ETF market heatmap Source: https://developer.hayinsights.com/api-reference/etf/etf-market-heatmap /openapi.yaml get /openapi/v1/etf/heatmap Snapshot of every tracked ETF for rendering a treemap heatmap — closing price, volume, market cap, price fluctuation and each fund's contribution to the aggregate index movement. `contributionArea` is the relative tile size. # ETF NAV growth Source: https://developer.hayinsights.com/api-reference/etf/etf-nav-growth /openapi.yaml get /openapi/v1/etf/nav-growth/{code} NAV-growth series for a single Vietnamese fund, benchmarked against the VN-Index, across the available period windows. Use the VN fund certificate code (e.g. `VESAF`) as the `{code}` path param. The response shares the comparison shape; the unused `etf2` slot has an empty `etfCode`. # ETF price history Source: https://developer.hayinsights.com/api-reference/etf/etf-price-history /openapi.yaml get /openapi/v1/etf/{code}/prices-history Daily OHLCV price history for a single ETF, ordered oldest to newest. Use the exchange ticker (e.g. a TSE code like `1306`) as the `{code}` path param. # List ETFs Source: https://developer.hayinsights.com/api-reference/etf/list-etfs /openapi.yaml get /openapi/v1/etf The full catalogue of tracked exchange-traded funds, with descriptive metadata (manager, benchmark index, trust fee), the latest price and assets under management. Use an entry's `etfCode` as the `{code}` path param for the info, holdings, prices-history and detail endpoints. # Top ETFs by net flow Source: https://developer.hayinsights.com/api-reference/etf/top-etfs-by-net-flow /openapi.yaml get /openapi/v1/etf/top-netflow ETFs ranked by net capital flow. Set `type` to `positive` for the largest inflows or `negative` for the largest outflows. # Top-performing ETFs Source: https://developer.hayinsights.com/api-reference/etf/top-performing-etfs /openapi.yaml get /openapi/v1/etf/top-performance ETFs ranked by NAV change over the requested period, best to worst. Use the `period` query parameter to choose the look-back window. # Emerging-market FX stress Source: https://developer.hayinsights.com/api-reference/fx/emerging-market-fx-stress /openapi.yaml get /openapi/v1/fx/em-stress Emerging-market FX stress monitor — for each tracked pair, compares recent realised volatility against its 90-day distribution and classifies the pair as STABLE, WARNING or PANIC. # FX correlation matrix Source: https://developer.hayinsights.com/api-reference/fx/fx-correlation-matrix /openapi.yaml get /openapi/v1/fx/correlation Rolling 90-day correlation matrix across major FX, crypto and gold benchmarks. `matrix[i][j]` is the Pearson correlation between `symbols[i]` and `symbols[j]`. # FX summary Source: https://developer.hayinsights.com/api-reference/fx/fx-summary /openapi.yaml get /openapi/v1/fx/summary FX dashboard summary — the US Dollar Index (DXY), the USD/JPY rate (each with a 1-day change) and a headline emerging-market FX stress reading. # FX table Source: https://developer.hayinsights.com/api-reference/fx/fx-table /openapi.yaml get /openapi/v1/fx/table Live table of tracked currency pairs — latest price, 1-day change, a 10-day sparkline and the 52-week high / low for each. # Central government debt (% of GDP) Source: https://developer.hayinsights.com/api-reference/macro/central-government-debt-%-of-gdp /openapi.yaml get /openapi/v1/macro/monetary-policy/central-government-debt Quarterly central-government debt expressed as a percentage of GDP for a country, with the year-over-year change in the ratio. # Economic data series Source: https://developer.hayinsights.com/api-reference/macro/economic-data-series /openapi.yaml get /openapi/v1/macro/economic-data Time series for a selected macroeconomic indicator — GDP, CPI, PCE or import-export — for a country, over a chosen look-back window. # Exchange rate series Source: https://developer.hayinsights.com/api-reference/macro/exchange-rate-series /openapi.yaml get /openapi/v1/macro/exchange-rate Daily closing exchange-rate history for a currency pair. Optionally limit the look-back window or return only data before a given date. # GDP growth rate Source: https://developer.hayinsights.com/api-reference/macro/gdp-growth-rate /openapi.yaml get /openapi/v1/macro/gdp-growth-rate Quarterly nominal GDP and its period-over-period growth rate for a country. Each point also carries the value's unit and the start/end of the quarter. # Latest macroeconomic market recap Source: https://developer.hayinsights.com/api-reference/macro/latest-macroeconomic-market-recap /openapi.yaml get /openapi/v1/macro/market-recap A point-in-time market recap bundling the latest Japanese equity / commodity indices, REITs, TOPIX, 3-month interbank reference rates and key currency rates into a single payload. # Policy rates & bond yields Source: https://developer.hayinsights.com/api-reference/macro/policy-rates-&-bond-yields /openapi.yaml get /openapi/v1/macro/monetary-policy/rates-yields Monthly policy / overnight call rate alongside the 10-year government bond yield for a country, over a chosen look-back window. # Population & unemployment Source: https://developer.hayinsights.com/api-reference/macro/population-&-unemployment /openapi.yaml get /openapi/v1/macro/population Monthly total-population figures alongside the unemployment rate for a country, over a chosen look-back window. # Country land-price statistics Source: https://developer.hayinsights.com/api-reference/real-estate/country-land-price-statistics /openapi.yaml get /openapi/v1/real-estate/{country}/stats Aggregate land-price statistics for a country — overall ward / segment totals, the observed price range, per-area breakdowns and the highest-priced roads. Data is available for `vn`; `jp` is empty. # Country real-estate summary Source: https://developer.hayinsights.com/api-reference/real-estate/country-real-estate-summary /openapi.yaml get /openapi/v1/real-estate/{country} Top-level real-estate summary for a country. Currently returns an empty array on staging — use the `stats`, `wards`, `land-prices`, `provinces` and `geojson` sub-routes for detailed data. Data is available for `vn`; `jp` is empty. # List priced road segments Source: https://developer.hayinsights.com/api-reference/real-estate/list-priced-road-segments /openapi.yaml get /openapi/v1/real-estate/{country}/land-prices Priced road segments for a country, with residential, commercial and production prices across value-position tiers. Filter by land-use `type`, `area`, `ward_id` or a free-text search, and cap the result count with `limit`. Data is available for `vn`. # List provinces Source: https://developer.hayinsights.com/api-reference/real-estate/list-provinces /openapi.yaml get /openapi/v1/real-estate/{country}/provinces Provinces / cities available for real-estate queries in a country. Data is available for `vn`; `jp` is empty. # List ward land-price summaries Source: https://developer.hayinsights.com/api-reference/real-estate/list-ward-land-price-summaries /openapi.yaml get /openapi/v1/real-estate/{country}/wards Ward-level land-price summaries for a country — average residential prices across value-position tiers, min/max prices and road counts. Data is available for `vn`; `jp` is empty. # Ward choropleth GeoJSON Source: https://developer.hayinsights.com/api-reference/real-estate/ward-choropleth-geojson /openapi.yaml get /openapi/v1/real-estate/{country}/geojson Ward boundary geometry paired with average-price attributes, for rendering a choropleth map. The response can be large (boundary coordinates for every ward); the geometry is modelled permissively. Data is available for `vn`; `jp` is empty. # Ward land-price detail Source: https://developer.hayinsights.com/api-reference/real-estate/ward-land-price-detail /openapi.yaml get /openapi/v1/real-estate/{country}/wards/{wardId} Full land-price detail for a single ward — an aggregate summary plus every priced road segment with residential, commercial and production prices across value-position tiers. Use the `wardId` returned by the wards list. Data is available for `vn`. # Authentication Source: https://developer.hayinsights.com/authentication A single API key in the X-API-Key header — no request signing. The HayInsights Open API uses a **single authentication tier**: send your API key in the `X-API-Key` header on every request to `/openapi/v1/*`. There is no HMAC signing, no timestamp, and no nonce. ```bash theme={null} curl -H "X-API-Key: $HAYINSIGHTS_API_KEY" \ "https://api-v2.hayinsights.com/openapi/v1/fx/summary" ``` | Header | When | Value | | ----------- | ------ | ------------------------------ | | `X-API-Key` | Always | Your API key (prefixed `apk_`) | The API key is only accepted on the `/openapi/v1/*` surface. The dashboard surface (`/api/v1/*`) uses a separate session-based login, so an API key can never be used to manage itself. ## Managing keys Create, rotate and revoke keys from the [HayInsights dashboard](https://app.hayinsights.com) under **Account → API keys**. Generate a key — the full secret (`apk_…`) is shown **once**. Copy it into a secret store or environment variable immediately. Rotating issues a new key and revokes the old one in a single step. Update your environment with the new secret. Revoking disables the key immediately on the next request. Treat your API key like a password. Never commit it to source control or embed it in client-side code. Keep it in an environment variable or secret manager. ## Access & quota Your key inherits the **subscription plan** on your account. The plan determines both which data domains you can access and your request quota: * A request for a domain your plan doesn't include returns **403 `FEATURE_NOT_IN_PLAN`** — see [Plans & features](/plans-and-features). * Exceeding your per-minute weight budget returns **429 `RATE_LIMIT_EXCEEDED`** — see [Rate limits](/rate-limits). ## Authentication errors | Code | Meaning | | ------------------ | -------------------------------- | | `API_KEY_REQUIRED` | No `X-API-Key` header was sent. | | `API_KEY_INVALID` | The key is unknown or malformed. | | `API_KEY_DISABLED` | The key has been revoked. | | `API_KEY_EXPIRED` | The key has expired. | All four return HTTP `401`. See [Error codes](/errors) for the full reference. # Changelog Source: https://developer.hayinsights.com/changelog Notable changes to the HayInsights Open API. **Initial preview release.** * 40 read endpoints across six domains: Commodities, Crypto, ETF, FX, Macro and Real Estate. * Single-tier authentication via the `X-API-Key` header. * Weighted, per-minute rate limiting with `X-RateLimit-*` headers. * Plan-based feature gating (`_data` feature codes). * Standard response envelope `{ success, statusCode, data, meta }`. # Error codes Source: https://developer.hayinsights.com/errors HTTP status mapping, the error envelope, and how to handle errors. Errors carry two signals: * The **HTTP status** (4xx / 5xx). * An **error envelope** in the body. ```json theme={null} { "success": false, "statusCode": 401, "error": { "code": "API_KEY_INVALID", "message": "Invalid API key" }, "meta": { "timestamp": "2026-06-19T08:34:08.023Z" } } ``` `error.message` is usually a string, but for validation errors (400) it can be an **array** of messages. `error.code` is specific for authentication and quota errors; for validation and not-found it is `INTERNAL_ERROR` and the HTTP status carries the meaning. ## HTTP status reference | Status | Meaning | When | | ------ | --------------------- | ------------------------------------------------------ | | `200` | OK | Success — payload in `data`. | | `400` | Bad Request | Missing or invalid parameter. | | `401` | Unauthorized | Missing, invalid, disabled or expired API key. | | `403` | Forbidden | Your plan doesn't include this feature. | | `404` | Not Found | The requested resource does not exist. | | `429` | Too Many Requests | Rate limit exceeded — see [Rate limits](/rate-limits). | | `500` | Internal Server Error | Unexpected server error — retry with backoff. | ## Authentication errors (401) No `X-API-Key` header was sent. Every `/openapi/v1/*` request requires one. The key is unknown or malformed. Check for stray whitespace or a truncated value. The key has been revoked. Create a new one in the dashboard. The key has expired. Create a new one in the dashboard. ## Authorization & quota errors Your subscription plan doesn't include the requested data domain. Upgrade from the dashboard — see [Plans & features](/plans-and-features). You've consumed your plan's per-minute weight budget. Wait until `X-RateLimit-Reset` (or `Retry-After` seconds) then retry — see [Rate limits](/rate-limits). ## Validation & not-found (400 / 404) A required query parameter is missing or a value is out of range. `error.message` lists the specific problems, e.g. `"Type parameter is required"`. The path references something that doesn't exist (e.g. an unknown commodity slug or fund code). ## Handling strategy `2xx` → read `data`. `4xx` / `5xx` → read `error.code` to classify. Retry with backoff on `429` (until `Retry-After` / `X-RateLimit-Reset`) and `5xx` (exponential backoff, a few attempts). Do **not** retry `400`, `401` or `403` — fix the root cause first. Capture `error.code`, `error.message`, `meta.timestamp`, the HTTP status and the request path when reporting an issue. # Introduction Source: https://developer.hayinsights.com/introduction Programmatic access to the HayInsights financial-data hub. The **HayInsights Open API** gives you programmatic access to the HayInsights financial-data hub — a single API surface under `/openapi/v1/*` covering six data domains: Spot prices, top movers, gold–DXY correlation and historical series. Market overview, dominance, bubble map, on-chain and derivatives metrics. Listings, performance & net-flow rankings, holdings, NAV growth, composition. Summary, live table, 90-day correlation matrix and EM stress monitor. GDP, CPI/PCE, exchange rates, population, government debt, rates & yields. Statistics, land prices, provinces / wards and choropleth GeoJSON. ## Base URL ``` https://api-v2.hayinsights.com ``` A staging environment is available at `https://stg-api.hayinsights.com`. ## How it works * **One credential.** Send your API key in the `X-API-Key` header on every request. There is no request signing — see [Authentication](/authentication). * **Plan-based access.** Which data domains you can call and how much you can call them are governed by the subscription plan attached to your key — see [Plans & features](/plans-and-features). * **Weighted quota.** Each endpoint consumes a weighted amount of your per-minute budget; every response returns `X-RateLimit-*` headers — see [Rate limits](/rate-limits). * **Consistent envelope.** Every response is wrapped in `{ success, statusCode, data, meta }`. ## Response envelope ```json Success theme={null} { "success": true, "statusCode": 200, "data": { "...": "endpoint-specific payload" }, "meta": { "timestamp": "2026-06-19T08:31:59.478Z" } } ``` ```json Error theme={null} { "success": false, "statusCode": 401, "error": { "code": "API_KEY_INVALID", "message": "Invalid API key" }, "meta": { "timestamp": "2026-06-19T08:34:08.023Z" } } ``` Get an API key and fetch live data in under 5 minutes. # Plans & features Source: https://developer.hayinsights.com/plans-and-features How your subscription plan unlocks data domains. Access to each data domain is controlled by a **feature** on your subscription plan. Your API key inherits your account's plan, and entitlement is resolved live on every request — so plan changes take effect immediately, with no need to rotate your key. ## Feature codes Each domain maps to one feature code: | Domain | Feature code | Weight per call | | ----------- | ------------------ | --------------- | | Commodities | `commodities_data` | 3 | | Crypto | `crypto_data` | 3 | | ETF | `etf_data` | 8 | | FX | `fx_data` | 3 – 6 | | Macro | `macro_data` | 2 | | Real Estate | `realestate_data` | 2 | ## When a feature isn't in your plan Calling a domain your plan doesn't include returns **HTTP 403**: ```json theme={null} { "success": false, "statusCode": 403, "error": { "code": "FEATURE_NOT_IN_PLAN", "message": "Your plan does not include this feature" }, "meta": { "timestamp": "2026-06-19T08:34:08.100Z" } } ``` To unlock the domain, upgrade your plan from the [dashboard](https://app.hayinsights.com). The exact mapping of plans (and their per-minute quota) to features is managed in the dashboard and may change as new tiers are introduced. This page lists the feature codes; your account page shows the features and quota active on your current plan. ## How entitlement is resolved 1. Your `X-API-Key` identifies your account. 2. The account's **effective plan** is looked up. 3. The plan's **active features** determine which domains you may call, and its **weight budget** determines your [rate limit](/rate-limits). Because this happens per request, upgrading or downgrading is reflected instantly. # Quickstart Source: https://developer.hayinsights.com/quickstart Make your first request in under 5 minutes — just one header. Every HayInsights endpoint needs a single header, `X-API-Key`. No request signing, no token exchange. Sign in to the [HayInsights dashboard](https://app.hayinsights.com) and go to **Account → API keys** to create a key. It looks like `apk_…`. Store it as an environment variable: ```bash theme={null} export HAYINSIGHTS_API_KEY="apk_your_key_here" ``` Fetch crypto market dominance: ```bash cURL theme={null} curl -H "X-API-Key: $HAYINSIGHTS_API_KEY" \ "https://api-v2.hayinsights.com/openapi/v1/crypto/dominance" ``` ```typescript TypeScript theme={null} const res = await fetch( "https://api-v2.hayinsights.com/openapi/v1/crypto/dominance", { headers: { "X-API-Key": process.env.HAYINSIGHTS_API_KEY! } } ); const json = await res.json(); console.log(json.data); ``` ```python Python theme={null} import os, requests res = requests.get( "https://api-v2.hayinsights.com/openapi/v1/crypto/dominance", headers={"X-API-Key": os.environ["HAYINSIGHTS_API_KEY"]}, ) print(res.json()["data"]) ``` Every response uses the standard envelope. The payload is in `data`: ```json theme={null} { "success": true, "statusCode": 200, "data": { "totalMarketCap": 2157717188047.44, "coins": [ { "symbol": "BTC", "name": "Bitcoin", "dominancePercentage": 58.16, "marketCap": 1254870895730.76 }, { "symbol": "ETH", "name": "Ethereum", "dominancePercentage": 9.48, "marketCap": 204564386727.13 } ] }, "meta": { "timestamp": "2026-06-19T08:31:59.478Z" } } ``` `success: true` means the call worked. On an error, `success` is `false` and the details are in `error` — see [Error codes](/errors). Every response carries rate-limit headers so you can self-throttle: ``` X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 997 X-RateLimit-Reset: 1781857979 X-RateLimit-Weight-Used: 3 ``` See [Rate limits](/rate-limits) for the weighting model. ## Next steps How API keys work and how to rotate them. All 40 endpoints across the six data domains. Which plan unlocks which datasets. Weighted quota and the `X-RateLimit-*` headers. # Rate limits Source: https://developer.hayinsights.com/rate-limits A weighted, per-minute quota tied to your plan. HayInsights uses a **weighted, sliding-window** rate limit. Instead of counting requests, each call consumes a **weight** from your plan's per-minute budget. Heavier endpoints (large aggregates) cost more than light ones. ## Headers Every response — success or error — carries the current quota state: | Header | Meaning | | ------------------------- | ---------------------------------------------------- | | `X-RateLimit-Limit` | Your plan's total weight budget per 1-minute window. | | `X-RateLimit-Remaining` | Weight remaining in the current window. | | `X-RateLimit-Reset` | Unix timestamp (seconds) when the window resets. | | `X-RateLimit-Weight-Used` | Weight consumed by this request. | ``` X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 997 X-RateLimit-Reset: 1781857979 X-RateLimit-Weight-Used: 3 ``` ## Endpoint weights | Domain | Feature | Weight per call | | ----------- | ------------------ | ------------------------------------------------------- | | Macro | `macro_data` | 2 | | Real Estate | `realestate_data` | 2 | | Commodities | `commodities_data` | 3 | | Crypto | `crypto_data` | 3 | | FX | `fx_data` | 3 (`summary`, `table`) · 6 (`correlation`, `em-stress`) | | ETF | `etf_data` | 8 | Example: on a plan with a 1,000 weight/minute budget you can make roughly 333 macro calls (weight 2) **or** 125 ETF calls (weight 8) per minute. Mixing endpoints draws from the same shared budget. ## Exceeding the limit When you exhaust your budget the API returns **HTTP 429** with the `RATE_LIMIT_EXCEEDED` code and a `Retry-After` header (seconds to wait): ```json theme={null} { "success": false, "statusCode": 429, "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded" }, "meta": { "timestamp": "2026-06-19T08:34:09.000Z" } } ``` ## Handling 429 gracefully Read the headers and back off until the window resets. ```typescript TypeScript theme={null} async function get(url: string, key: string): Promise { const res = await fetch(url, { headers: { "X-API-Key": key } }); if (res.status === 429) { const wait = Number(res.headers.get("Retry-After") ?? 1); await new Promise((r) => setTimeout(r, wait * 1000)); return get(url, key); } return res; } ``` ```python Python theme={null} import time, requests def get(url, key): res = requests.get(url, headers={"X-API-Key": key}) if res.status_code == 429: time.sleep(int(res.headers.get("Retry-After", 1))) return get(url, key) return res ``` Watch `X-RateLimit-Remaining` and slow down **before** you hit zero, rather than reacting to 429s after the fact.