The API v1 foundation
This page documents the real, already-built response contract behind Global Metals Intelligence's market-data API. It is a description of a technical foundation, not a customer onboarding guide โ see current developer-access status below before building anything against it.
API versioning
The current, and so far only, version is v1, under the base path /api/v1. Every response carries its own apiVersion: "v1" field. A future breaking change would ship as a new version rather than silently altering what v1 already returns.
Standard response envelope
Every endpoint returns the same shape:
{
"apiVersion": "v1",
"status": "live" | "delayed" | "reference" | "historical" | "stale" | "unavailable" | "invalid_request" | "error",
"data": { /* the requested figure, or null */ } | null,
"error": { "code": "...", "message": "..." } | null,
"meta": { "requestedAt": "2026-01-01T00:00:00.000Z" }
}Whether data is fresh, aging, a reference figure, historical, or simply not available yet is reported inside the body via status โ it is a data-availability fact, not treated as an HTTP-level error. Only a malformed request (invalid_request, HTTP 400) or a genuine internal failure (error, HTTP 500) breaks from HTTP 200.
Timestamps, freshness, and unavailable/error behavior
Timestamps are ISO 8601 strings throughout โ an observation's observedAt and receivedAt, and the request's own meta.requestedAt. When no verified observation exists, the API returns status: "unavailable" with data: null โ never an estimated, rounded, or previously-cached figure presented as current. An internal failure returns a generic error status with a fixed { code, message }, never internal exception detail.
Numeric representation
Live/reference metal and FX figures are plain JSON numbers. Historical monthly prices are returned as exact decimal strings (e.g. "4073.000000") rather than a JSON number, since the underlying value is stored to a fixed decimal precision a binary floating-point number cannot always represent exactly โ parse it as a decimal, not a float, if precision matters to your use case.
Data provenance policy
Every price/rate object carries its own provider, source, and (for historical records) license field. This platform's public-facing surfaces โ this API included โ use the same generic "Platform Reference" label the rest of the site already uses for its live commercial data provider, rather than naming that provider directly here; see Sources & Data Methodology for the full, voluntary provider-by-provider disclosure this platform maintains.
Current developer-access status
API keys: a signed-in visitor can generate, name, and revoke their own developer API keys at API Keys. A key now genuinely unlocks ONE endpoint โ GET /api/v1/market/history/[metal] โ and nothing else. It is shown to you exactly once, at creation; GMI never stores or displays the plaintext value again, so save it somewhere safe.
Not yet available through this API, at any key status: current Gold price, current Silver price, and every currency-conversion pair except EUR-USD/USD-EUR. This is a deliberate licensing decision, not a bug or an oversight โ see Sources & Data Methodology for why. A valid key hitting a not-yet-available product returns HTTP 403, never fabricated or substituted data.
Authentication
Send your key as a bearer token, on the enforced endpoint only:
Authorization: Bearer gmi_<your-key>
Never as a query-string parameter โ a key in a URL ends up in server logs, browser history, and proxy caches. Missing, malformed, unknown, revoked, and expired keys all return the identical HTTP 401 body below, so a caller can never learn anything about a key it does not already hold.
{
"apiVersion": "v1",
"status": "error",
"data": null,
"error": { "code": "unauthorized", "message": "Missing or invalid API key." },
"meta": { "requestedAt": "2026-01-01T00:00:00.000Z" }
}Example request
curl "https://globalmetalsintelligence.com/api/v1/market/history/XAU?currency=USD&unit=troy_ounce" \ -H "Authorization: Bearer gmi_your_key_here"
(Placeholder key shown โ never a real one. Replace gmi_your_key_here with the value shown once when you created your key.)
Rate limits
60 requests per rolling 60-second window, per key, on the enforced endpoint. Every response on that endpoint carries RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers so you can back off before hitting the limit. Exceeding it returns HTTP 429 with a Retry-After header (seconds until the window resets) and error.code: "rate_limited". This is a single, technical V1 limit applied identically to every key โ not a published plan tier, and not a promise it will stay this exact number.
Security guidance
- Treat your key like a password. Never commit it, never put it in client-side/browser JavaScript, never share it.
- Revoking a key at API Keys is permanent โ a revoked key can never be un-revoked, and every request it makes afterward is rejected the same way an unknown key is.
- If a key is ever exposed, revoke it and create a new one. GMI cannot rotate a key's secret in place.