GMI
Menu
Metals feed: Loading
Documentation

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.