BinderDex API

Batch prices

Proposed POST /v1/prices/batch

POST/v1/prices/batch

Requires prices:read. Submit one explicit selection per requested lane. Card selections require both variant and condition; sealed selections have no implicit variant or condition. Exact duplicate selections are a whole-request 400 even if JSON property order differs. Same product with distinct selections is allowed. Input order is preserved.

Proposed access: send an X-API-Key header from a server-side integration. Keys are unavailable during preview.

Response shape

FieldTypeRequiredDescription
dataarray<PriceResult>YesminItems: 1 · maxItems: 50
data[].selectionPriceSelectionYesA requested price lane. Selection kind must match the verified product kind.
data[].selection (option 1).kindstringYes"card" const: card
data[].selection (option 1).product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
data[].selection (option 1).variantstringYesminLength: 1 · maxLength: 100
data[].selection (option 1).conditionstringYes["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"] enum: ["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"]
data[].selection (option 2).kindstringYes"sealed" const: sealed
data[].selection (option 2).product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
data[].statusstringYes["fresh","stale","unknown","missing"] enum: ["fresh","stale","unknown","missing"]
data[].priceMoney | nullYes
data[].price (option 1).amount_centsintegerYesminimum: 0 · maximum: 9007199254740991
data[].price (option 1).currencystringYes"USD" const: USD
data[].observationObservation | nullYes
data[].observation (option 1).sourcestringYes"licensed_reference" const: licensed_reference
data[].observation (option 1).source_as_ofstring | nullYesformat: date-time · pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$
data[].observation (option 1).collected_atstringYesformat: date-time · pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$
data[].observation (option 1).basisstringYes"market_reference" const: market_reference
data[].observation (option 1).attributionstringYes
data[].missing_reasonstring | nullYes["not_found","no_observation","not_licensed","unsupported_selection",null] enum: ["not_found","no_observation","not_licensed","unsupported_selection",null]
metaMetaYesFreshness is evaluated at snapshot_at, which stays original on a batch replay. Other metadata is generated per response. Proposed commercial semantics: successful data calls may consume one request unit, including empty or missing data; replays and errors consume zero. additionalProperties: false
meta.request_idstringYespattern: ^req_[a-zA-Z0-9_-]+$
meta.snapshot_atstringYesformat: date-time · pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$
meta.billable_unitsintegerYes[0,1] enum: [0,1]
meta.idempotent_replaybooleanYes

Response headers

HeaderTypePreview meaning
X-Request-IdstringRequest correlation identifier; safe to send to support.
X-Request-Credits-RemainingintegerOrganization credits remaining after this request. Present only after successful key authentication.
X-Request-Credits-Chargedinteger0 or 1 for this response; internal cache hits count, idempotent batch replays do not.
Cache-Controlstringprivate, no-store; key-specific metadata must never enter a shared HTTP cache. Server-side data caching is separate.

Parameters

NameInTypeRequiredDescription
Idempotency-KeyheaderstringYesRequired. Organization+operation-scoped, body-bound key retained 10 minutes. Same normalized body within retention returns original data/snapshot, charged 0 even at quota exhaustion (still rate limited and reauthorized). Different body returns 409. After retention a key is a new request; use a new key for each nightly job. Failed requests are not retained. pattern: ^[A-Za-z0-9_-]{1,64}$

Request body

One to fifty explicit selections. No variant or condition defaults are inferred for raw cards.

FieldTypeRequiredDescription
itemsarray<PriceSelection>YesExact duplicate selections are invalid even if JSON property order differs. The same product with a different explicit variant or condition is allowed. minItems: 1 · maxItems: 50 · uniqueItems: true
items[] (option 1).kindstringYes"card" const: card
items[] (option 1).product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
items[] (option 1).variantstringYesminLength: 1 · maxLength: 100
items[] (option 1).conditionstringYes["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"] enum: ["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"]
items[] (option 2).kindstringYes"sealed" const: sealed
items[] (option 2).product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
Synthetic request bodyjson
{
  "items": [
    {
      "kind": "card",
      "product_id": "bdp_demo_pikachu_en",
      "variant": "normal",
      "condition": "near_mint"
    },
    {
      "kind": "card",
      "product_id": "bdp_demo_luffy_en",
      "variant": "foil",
      "condition": "near_mint"
    },
    {
      "kind": "sealed",
      "product_id": "bdp_demo_151_bundle_en"
    }
  ]
}

Synthetic example

Synthetic response — not a live API responsejson
{
  "data": [
    {
      "selection": {
        "kind": "card",
        "product_id": "bdp_demo_pikachu_en",
        "variant": "normal",
        "condition": "near_mint"
      },
      "status": "fresh",
      "price": {
        "amount_cents": 1250,
        "currency": "USD"
      },
      "observation": {
        "source": "licensed_reference",
        "source_as_of": "2026-09-06T10:00:00Z",
        "collected_at": "2026-09-06T11:00:00Z",
        "basis": "market_reference",
        "attribution": "Synthetic preview observation — no provider data."
      },
      "missing_reason": null
    },
    {
      "selection": {
        "kind": "card",
        "product_id": "bdp_demo_luffy_en",
        "variant": "foil",
        "condition": "near_mint"
      },
      "status": "stale",
      "price": {
        "amount_cents": 2080,
        "currency": "USD"
      },
      "observation": {
        "source": "licensed_reference",
        "source_as_of": "2026-09-04T00:00:00Z",
        "collected_at": "2026-09-04T01:00:00Z",
        "basis": "market_reference",
        "attribution": "Synthetic preview observation — no provider data."
      },
      "missing_reason": null
    },
    {
      "selection": {
        "kind": "sealed",
        "product_id": "bdp_demo_151_bundle_en"
      },
      "status": "missing",
      "price": null,
      "observation": null,
      "missing_reason": "no_observation"
    }
  ],
  "meta": {
    "request_id": "req_example",
    "snapshot_at": "2026-09-06T12:00:00Z",
    "billable_units": 1,
    "idempotent_replay": false
  }
}

Nested definitions

These are the exact reusable shapes referenced by this operation. Constraints are derived from the preview contract.

BatchRequestSchema definition
FieldTypeRequiredDescription
itemsarray<PriceSelection>YesExact duplicate selections are invalid even if JSON property order differs. The same product with a different explicit variant or condition is allowed. minItems: 1 · maxItems: 50 · uniqueItems: true
items[] (option 1).kindstringYes"card" const: card
items[] (option 1).product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
items[] (option 1).variantstringYesminLength: 1 · maxLength: 100
items[] (option 1).conditionstringYes["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"] enum: ["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"]
items[] (option 2).kindstringYes"sealed" const: sealed
items[] (option 2).product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
Full canonical schema JSON
BatchRequest schema from the preview contractjson
{
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "minItems": 1,
      "maxItems": 50,
      "uniqueItems": true,
      "items": {
        "$ref": "#/components/schemas/PriceSelection"
      },
      "description": "Exact duplicate selections are invalid even if JSON property order differs. The same product with a different explicit variant or condition is allowed."
    }
  },
  "required": [
    "items"
  ],
  "additionalProperties": false
}
BatchResponseOne result per requested selection in input order. Unknown IDs are per-item missing/not_found. Kind mismatches and unsupported verified raw-card lanes are per-item missing/unsupported_selection. Exact duplicate selections are a whole-request 400.
FieldTypeRequiredDescription
dataarray<PriceResult>YesminItems: 1 · maxItems: 50
data[].selectionPriceSelectionYesA requested price lane. Selection kind must match the verified product kind.
data[].selection (option 1).kindstringYes"card" const: card
data[].selection (option 1).product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
data[].selection (option 1).variantstringYesminLength: 1 · maxLength: 100
data[].selection (option 1).conditionstringYes["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"] enum: ["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"]
data[].selection (option 2).kindstringYes"sealed" const: sealed
data[].selection (option 2).product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
data[].statusstringYes["fresh","stale","unknown","missing"] enum: ["fresh","stale","unknown","missing"]
data[].priceMoney | nullYes
data[].price (option 1).amount_centsintegerYesminimum: 0 · maximum: 9007199254740991
data[].price (option 1).currencystringYes"USD" const: USD
data[].observationObservation | nullYes
data[].observation (option 1).sourcestringYes"licensed_reference" const: licensed_reference
data[].observation (option 1).source_as_ofstring | nullYesformat: date-time · pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$
data[].observation (option 1).collected_atstringYesformat: date-time · pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$
data[].observation (option 1).basisstringYes"market_reference" const: market_reference
data[].observation (option 1).attributionstringYes
data[].missing_reasonstring | nullYes["not_found","no_observation","not_licensed","unsupported_selection",null] enum: ["not_found","no_observation","not_licensed","unsupported_selection",null]
metaMetaYesFreshness is evaluated at snapshot_at, which stays original on a batch replay. Other metadata is generated per response. Proposed commercial semantics: successful data calls may consume one request unit, including empty or missing data; replays and errors consume zero. additionalProperties: false
meta.request_idstringYespattern: ^req_[a-zA-Z0-9_-]+$
meta.snapshot_atstringYesformat: date-time · pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$
meta.billable_unitsintegerYes[0,1] enum: [0,1]
meta.idempotent_replaybooleanYes
Full canonical schema JSON
BatchResponse schema from the preview contractjson
{
  "type": "object",
  "properties": {
    "data": {
      "type": "array",
      "minItems": 1,
      "maxItems": 50,
      "items": {
        "$ref": "#/components/schemas/PriceResult"
      }
    },
    "meta": {
      "$ref": "#/components/schemas/Meta"
    }
  },
  "required": [
    "data",
    "meta"
  ],
  "additionalProperties": false,
  "description": "One result per requested selection in input order. Unknown IDs are per-item missing/not_found. Kind mismatches and unsupported verified raw-card lanes are per-item missing/unsupported_selection. Exact duplicate selections are a whole-request 400."
}
PriceSelectionA requested price lane. Selection kind must match the verified product kind.
FieldTypeRequiredDescription
option 1.kindstringYes"card" const: card
option 1.product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
option 1.variantstringYesminLength: 1 · maxLength: 100
option 1.conditionstringYes["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"] enum: ["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"]
option 2.kindstringYes"sealed" const: sealed
option 2.product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
Full canonical schema JSON
PriceSelection schema from the preview contractjson
{
  "oneOf": [
    {
      "$ref": "#/components/schemas/CardPriceSelection"
    },
    {
      "$ref": "#/components/schemas/SealedPriceSelection"
    }
  ],
  "discriminator": {
    "propertyName": "kind",
    "mapping": {
      "card": "#/components/schemas/CardPriceSelection",
      "sealed": "#/components/schemas/SealedPriceSelection"
    }
  },
  "description": "A requested price lane. Selection kind must match the verified product kind."
}
CardPriceSelectionExplicit raw-card price lane. Ungraded is distinct from near_mint and is never converted.
FieldTypeRequiredDescription
kindstringYes"card" const: card
product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
variantstringYesminLength: 1 · maxLength: 100
conditionstringYes["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"] enum: ["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"]
Full canonical schema JSON
CardPriceSelection schema from the preview contractjson
{
  "type": "object",
  "properties": {
    "kind": {
      "type": "string",
      "const": "card"
    },
    "product_id": {
      "type": "string",
      "pattern": "^bdp_[a-z0-9_]{3,64}$",
      "description": "Opaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic."
    },
    "variant": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "condition": {
      "type": "string",
      "enum": [
        "near_mint",
        "lightly_played",
        "moderately_played",
        "heavily_played",
        "damaged",
        "ungraded"
      ]
    }
  },
  "required": [
    "kind",
    "product_id",
    "variant",
    "condition"
  ],
  "additionalProperties": false,
  "description": "Explicit raw-card price lane. Ungraded is distinct from near_mint and is never converted."
}
SealedPriceSelectionExplicit sealed-product selection. No raw-card variant or condition applies.
FieldTypeRequiredDescription
kindstringYes"sealed" const: sealed
product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
Full canonical schema JSON
SealedPriceSelection schema from the preview contractjson
{
  "type": "object",
  "properties": {
    "kind": {
      "type": "string",
      "const": "sealed"
    },
    "product_id": {
      "type": "string",
      "pattern": "^bdp_[a-z0-9_]{3,64}$",
      "description": "Opaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic."
    }
  },
  "required": [
    "kind",
    "product_id"
  ],
  "additionalProperties": false,
  "description": "Explicit sealed-product selection. No raw-card variant or condition applies."
}
PriceResultOne result per selection in input order. fresh: source_as_of <=36h old at snapshot_at; stale: >36h; unknown: price exists but source_as_of is null; missing: no distributable price. A kind mismatch or unsupported verified variant/condition lane is per-item missing/unsupported_selection; unknown ID is per-item missing/not_found. No synthetic fills or provider blending.
FieldTypeRequiredDescription
selectionPriceSelectionYesA requested price lane. Selection kind must match the verified product kind.
selection (option 1).kindstringYes"card" const: card
selection (option 1).product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
selection (option 1).variantstringYesminLength: 1 · maxLength: 100
selection (option 1).conditionstringYes["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"] enum: ["near_mint","lightly_played","moderately_played","heavily_played","damaged","ungraded"]
selection (option 2).kindstringYes"sealed" const: sealed
selection (option 2).product_idstringYesOpaque immutable BinderDex public product ID. Never parse it; slugs and upstream IDs are aliases, not identity. Demo IDs and mappings are synthetic. pattern: ^bdp_[a-z0-9_]{3,64}$
statusstringYes["fresh","stale","unknown","missing"] enum: ["fresh","stale","unknown","missing"]
priceMoney | nullYes
price (option 1).amount_centsintegerYesminimum: 0 · maximum: 9007199254740991
price (option 1).currencystringYes"USD" const: USD
observationObservation | nullYes
observation (option 1).sourcestringYes"licensed_reference" const: licensed_reference
observation (option 1).source_as_ofstring | nullYesformat: date-time · pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$
observation (option 1).collected_atstringYesformat: date-time · pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$
observation (option 1).basisstringYes"market_reference" const: market_reference
observation (option 1).attributionstringYes
missing_reasonstring | nullYes["not_found","no_observation","not_licensed","unsupported_selection",null] enum: ["not_found","no_observation","not_licensed","unsupported_selection",null]
Full canonical schema JSON
PriceResult schema from the preview contractjson
{
  "type": "object",
  "properties": {
    "selection": {
      "$ref": "#/components/schemas/PriceSelection"
    },
    "status": {
      "type": "string",
      "enum": [
        "fresh",
        "stale",
        "unknown",
        "missing"
      ]
    },
    "price": {
      "oneOf": [
        {
          "$ref": "#/components/schemas/Money"
        },
        {
          "type": "null"
        }
      ]
    },
    "observation": {
      "oneOf": [
        {
          "$ref": "#/components/schemas/Observation"
        },
        {
          "type": "null"
        }
      ]
    },
    "missing_reason": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "not_found",
        "no_observation",
        "not_licensed",
        "unsupported_selection",
        null
      ]
    }
  },
  "required": [
    "selection",
    "status",
    "price",
    "observation",
    "missing_reason"
  ],
  "additionalProperties": false,
  "description": "One result per selection in input order. fresh: source_as_of <=36h old at snapshot_at; stale: >36h; unknown: price exists but source_as_of is null; missing: no distributable price. A kind mismatch or unsupported verified variant/condition lane is per-item missing/unsupported_selection; unknown ID is per-item missing/not_found. No synthetic fills or provider blending.",
  "allOf": [
    {
      "if": {
        "properties": {
          "status": {
            "const": "missing"
          }
        }
      },
      "then": {
        "properties": {
          "price": {
            "type": "null"
          },
          "observation": {
            "type": "null"
          },
          "missing_reason": {
            "type": "string",
            "enum": [
              "not_found",
              "no_observation",
              "not_licensed",
              "unsupported_selection"
            ]
          }
        }
      },
      "else": {
        "properties": {
          "price": {
            "$ref": "#/components/schemas/Money"
          },
          "observation": {
            "$ref": "#/components/schemas/Observation"
          },
          "missing_reason": {
            "type": "null"
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "status": {
            "const": "unknown"
          }
        }
      },
      "then": {
        "properties": {
          "observation": {
            "properties": {
              "source_as_of": {
                "type": "null"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "status": {
            "enum": [
              "fresh",
              "stale"
            ]
          }
        }
      },
      "then": {
        "properties": {
          "observation": {
            "properties": {
              "source_as_of": {
                "type": "string",
                "format": "date-time",
                "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,3})?Z$"
              }
            }
          }
        }
      }
    }
  ]
}
MoneyInteger US cents. No foreign-exchange conversion; null price means unknown, never zero. Zero is a valid observed amount.
FieldTypeRequiredDescription
amount_centsintegerYesminimum: 0 · maximum: 9007199254740991
currencystringYes"USD" const: USD
Full canonical schema JSON
Money schema from the preview contractjson
{
  "type": "object",
  "properties": {
    "amount_cents": {
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "currency": {
      "type": "string",
      "const": "USD"
    }
  },
  "required": [
    "amount_cents",
    "currency"
  ],
  "additionalProperties": false,
  "description": "Integer US cents. No foreign-exchange conversion; null price means unknown, never zero. Zero is a valid observed amount."
}
ObservationSource label and attribution must be contractually permitted. source_as_of is the provider observation time, never import time. Mock observations are wholly synthetic. Freshness unknown when source_as_of is absent even if collection time is recent. collected_at is required when an observation exists. Temporal invariant: source_as_of (when known) <= collected_at <= meta.snapshot_at; quarantine invalid upstream records. licensed_reference is a synthetic placeholder; finalize the stable licensed source identifier and attribution before public v1.
FieldTypeRequiredDescription
sourcestringYes"licensed_reference" const: licensed_reference
source_as_ofstring | nullYesformat: date-time · pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$
collected_atstringYesformat: date-time · pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$
basisstringYes"market_reference" const: market_reference
attributionstringYes
Full canonical schema JSON
Observation schema from the preview contractjson
{
  "type": "object",
  "properties": {
    "source": {
      "type": "string",
      "const": "licensed_reference"
    },
    "source_as_of": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,3})?Z$"
    },
    "collected_at": {
      "type": "string",
      "format": "date-time",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,3})?Z$"
    },
    "basis": {
      "type": "string",
      "const": "market_reference"
    },
    "attribution": {
      "type": "string"
    }
  },
  "required": [
    "source",
    "source_as_of",
    "collected_at",
    "basis",
    "attribution"
  ],
  "additionalProperties": false,
  "description": "Source label and attribution must be contractually permitted. source_as_of is the provider observation time, never import time. Mock observations are wholly synthetic. Freshness unknown when source_as_of is absent even if collection time is recent. collected_at is required when an observation exists. Temporal invariant: source_as_of (when known) <= collected_at <= meta.snapshot_at; quarantine invalid upstream records. licensed_reference is a synthetic placeholder; finalize the stable licensed source identifier and attribution before public v1."
}
MetaFreshness is evaluated at snapshot_at, which stays original on a batch replay. Other metadata is generated per response. Proposed commercial semantics: successful data calls may consume one request unit, including empty or missing data; replays and errors consume zero.
FieldTypeRequiredDescription
request_idstringYespattern: ^req_[a-zA-Z0-9_-]+$
snapshot_atstringYesformat: date-time · pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$
billable_unitsintegerYes[0,1] enum: [0,1]
idempotent_replaybooleanYes
Full canonical schema JSON
Meta schema from the preview contractjson
{
  "type": "object",
  "properties": {
    "request_id": {
      "type": "string",
      "pattern": "^req_[a-zA-Z0-9_-]+$"
    },
    "snapshot_at": {
      "type": "string",
      "format": "date-time",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,3})?Z$"
    },
    "billable_units": {
      "type": "integer",
      "enum": [
        0,
        1
      ]
    },
    "idempotent_replay": {
      "type": "boolean"
    }
  },
  "required": [
    "request_id",
    "snapshot_at",
    "billable_units",
    "idempotent_replay"
  ],
  "additionalProperties": false,
  "description": "Freshness is evaluated at snapshot_at, which stays original on a batch replay. Other metadata is generated per response. Proposed commercial semantics: successful data calls may consume one request unit, including empty or missing data; replays and errors consume zero."
}
ErrorNo secrets, SQL, or stack traces. retry_after_seconds is non-null for 429/503 and matches Retry-After. Retry rate/503 with bounded backoff and jitter; quota retry waits until period_end or explicit upgrade. 401/403/400/409 are not blind-retryable.
FieldTypeRequiredDescription
errorobjectYesadditionalProperties: false
error.codestringYes["invalid_request","invalid_api_key","insufficient_scope","not_found","idempotency_conflict","payload_too_large","unsupported_media_type","rate_limit_exceeded","quota_exceeded","temporarily_unavailable","internal_error"] enum: ["invalid_request","invalid_api_key","insufficient_scope","not_found","idempotency_conflict","payload_too_large","unsupported_media_type","rate_limit_exceeded","quota_exceeded","temporarily_unavailable","internal_error"]
error.messagestringYes
error.request_idstringYespattern: ^req_[a-zA-Z0-9_-]+$
error.retry_after_secondsinteger | nullYesminimum: 0
Full canonical schema JSON
Error schema from the preview contractjson
{
  "type": "object",
  "properties": {
    "error": {
      "type": "object",
      "properties": {
        "code": {
          "type": "string",
          "enum": [
            "invalid_request",
            "invalid_api_key",
            "insufficient_scope",
            "not_found",
            "idempotency_conflict",
            "payload_too_large",
            "unsupported_media_type",
            "rate_limit_exceeded",
            "quota_exceeded",
            "temporarily_unavailable",
            "internal_error"
          ]
        },
        "message": {
          "type": "string"
        },
        "request_id": {
          "type": "string",
          "pattern": "^req_[a-zA-Z0-9_-]+$"
        },
        "retry_after_seconds": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0
        }
      },
      "required": [
        "code",
        "message",
        "request_id",
        "retry_after_seconds"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "error"
  ],
  "additionalProperties": false,
  "description": "No secrets, SQL, or stack traces. retry_after_seconds is non-null for 429/503 and matches Retry-After. Retry rate/503 with bounded backoff and jitter; quota retry waits until period_end or explicit upgrade. 401/403/400/409 are not blind-retryable."
}

Possible errors

Every error below uses the Error envelope with a request ID. Retry only when the response’s proposed semantics identify a temporary condition; see errors and versioning for the detailed guidance.

400 synthetic error example

Invalid or unknown parameter/body, duplicated query parameter, malformed or expired cursor.

X-Request-Id — Request correlation identifier; safe to send to support. X-Request-Credits-Remaining — Organization credits remaining after this request. Present only after successful key authentication. X-Request-Credits-Charged — 0 or 1 for this response; internal cache hits count, idempotent batch replays do not. Cache-Control — private, no-store; key-specific metadata must never enter a shared HTTP cache. Server-side data caching is separate.

Synthetic 400 error — not a live API responsejson
{
  "error": {
    "code": "invalid_request",
    "message": "Synthetic example: Invalid or unknown parameter/body, duplicated query parameter, malformed or expired cursor.",
    "request_id": "req_example",
    "retry_after_seconds": null
  }
}
401 synthetic error example

Missing, invalid, expired or revoked API key.

X-Request-Id — Request correlation identifier; safe to send to support. X-Request-Credits-Remaining — Organization credits remaining after this request. Present only after successful key authentication. X-Request-Credits-Charged — 0 or 1 for this response; internal cache hits count, idempotent batch replays do not. Cache-Control — private, no-store; key-specific metadata must never enter a shared HTTP cache. Server-side data caching is separate.

Synthetic 401 error — not a live API responsejson
{
  "error": {
    "code": "invalid_api_key",
    "message": "Synthetic example: Missing, invalid, expired or revoked API key.",
    "request_id": "req_example",
    "retry_after_seconds": null
  }
}
403 synthetic error example

Authenticated key lacks endpoint scope or entitlement.

X-Request-Id — Request correlation identifier; safe to send to support. X-Request-Credits-Remaining — Organization credits remaining after this request. Present only after successful key authentication. X-Request-Credits-Charged — 0 or 1 for this response; internal cache hits count, idempotent batch replays do not. Cache-Control — private, no-store; key-specific metadata must never enter a shared HTTP cache. Server-side data caching is separate.

Synthetic 403 error — not a live API responsejson
{
  "error": {
    "code": "insufficient_scope",
    "message": "Synthetic example: Authenticated key lacks endpoint scope or entitlement.",
    "request_id": "req_example",
    "retry_after_seconds": null
  }
}
409 synthetic error example

Idempotency key reused with a different normalized batch body.

X-Request-Id — Request correlation identifier; safe to send to support. X-Request-Credits-Remaining — Organization credits remaining after this request. Present only after successful key authentication. X-Request-Credits-Charged — 0 or 1 for this response; internal cache hits count, idempotent batch replays do not. Cache-Control — private, no-store; key-specific metadata must never enter a shared HTTP cache. Server-side data caching is separate.

Synthetic 409 error — not a live API responsejson
{
  "error": {
    "code": "idempotency_conflict",
    "message": "Synthetic example: Idempotency key reused with a different normalized batch body.",
    "request_id": "req_example",
    "retry_after_seconds": null
  }
}
413 synthetic error example

JSON body exceeds 16 KiB.

X-Request-Id — Request correlation identifier; safe to send to support. X-Request-Credits-Remaining — Organization credits remaining after this request. Present only after successful key authentication. X-Request-Credits-Charged — 0 or 1 for this response; internal cache hits count, idempotent batch replays do not. Cache-Control — private, no-store; key-specific metadata must never enter a shared HTTP cache. Server-side data caching is separate.

Synthetic 413 error — not a live API responsejson
{
  "error": {
    "code": "payload_too_large",
    "message": "Synthetic example: JSON body exceeds 16 KiB.",
    "request_id": "req_example",
    "retry_after_seconds": null
  }
}
415 synthetic error example

Content-Type must be application/json.

X-Request-Id — Request correlation identifier; safe to send to support. X-Request-Credits-Remaining — Organization credits remaining after this request. Present only after successful key authentication. X-Request-Credits-Charged — 0 or 1 for this response; internal cache hits count, idempotent batch replays do not. Cache-Control — private, no-store; key-specific metadata must never enter a shared HTTP cache. Server-side data caching is separate.

Synthetic 415 error — not a live API responsejson
{
  "error": {
    "code": "unsupported_media_type",
    "message": "Synthetic example: Content-Type must be application/json.",
    "request_id": "req_example",
    "retry_after_seconds": null
  }
}
429 synthetic error example

rate_limit_exceeded or quota_exceeded; distinct codes and Retry-After.

X-Request-Id — Request correlation identifier; safe to send to support. X-Request-Credits-Remaining — Organization credits remaining after this request. Present only after successful key authentication. X-Request-Credits-Charged — 0 or 1 for this response; internal cache hits count, idempotent batch replays do not. Cache-Control — private, no-store; key-specific metadata must never enter a shared HTTP cache. Server-side data caching is separate. Retry-After — Whole seconds until retry.

Synthetic 429 error — not a live API responsejson
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Synthetic example: rate_limit_exceeded or quota_exceeded; distinct codes and Retry-After.",
    "request_id": "req_example",
    "retry_after_seconds": 60
  }
}
500 synthetic error example

Unexpected internal failure; no sensitive details and zero credits.

X-Request-Id — Request correlation identifier; safe to send to support. X-Request-Credits-Remaining — Organization credits remaining after this request. Present only after successful key authentication. X-Request-Credits-Charged — 0 or 1 for this response; internal cache hits count, idempotent batch replays do not. Cache-Control — private, no-store; key-specific metadata must never enter a shared HTTP cache. Server-side data caching is separate.

Synthetic 500 error — not a live API responsejson
{
  "error": {
    "code": "internal_error",
    "message": "Synthetic example: Unexpected internal failure; no sensitive details and zero credits.",
    "request_id": "req_example",
    "retry_after_seconds": null
  }
}
503 synthetic error example

temporarily_unavailable: metering, key state, or serving dependency unavailable; fail closed and charge zero.

X-Request-Id — Request correlation identifier; safe to send to support. X-Request-Credits-Remaining — Organization credits remaining after this request. Present only after successful key authentication. X-Request-Credits-Charged — 0 or 1 for this response; internal cache hits count, idempotent batch replays do not. Cache-Control — private, no-store; key-specific metadata must never enter a shared HTTP cache. Server-side data caching is separate. Retry-After — Whole seconds until retry.

Synthetic 503 error — not a live API responsejson
{
  "error": {
    "code": "temporarily_unavailable",
    "message": "Synthetic example: temporarily_unavailable: metering, key state, or serving dependency unavailable; fail closed and charge zero.",
    "request_id": "req_example",
    "retry_after_seconds": 30
  }
}