> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cromos.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Look up prices in bulk

> Price a whole collection in one request, with an acquisition date per card.

Send the printings you hold and get one priced row back for each, in the order you sent them. Up
to 500 per request, so a 1,000-card collection is two calls rather than a thousand.

## Identify a printing, not a card

`card_variant_id` is the `id` on a card's `variants`, from [Get a card](/cards/get). A card id
alone is not enough: one card can carry several printings, they are priced separately, and they
move independently. Pair it with the `condition` you own. The default is `NM`.

## One date per item

`since` rides on the item, not on the request. Two copies of one card acquired six months apart
are two entries with two different `since` values, and each gets its own `change`. Repeat the same
`card_variant_id` as many times as you hold copies.

`change_bp` is basis points: `1293` is `+12.93%`. It is rounded to whole basis points and every
amount is an integer number of cents, so no float reaches you and nothing is left for your client
to round.

## Reading an answer that is missing

Four absences mean four different things, and an app usually shows something different for each:

| What you see                         | What it means                                              |
| ------------------------------------ | ---------------------------------------------------------- |
| `card_id` is `null`                  | No printing carries that `card_variant_id`.                |
| `market` is `null`, `card_id` is set | That printing has no price in the condition you asked for. |
| `change` is absent                   | You did not send `since` for this item.                    |
| `change` is `null`                   | You did, and no change can be worked out.                  |

The last one is common for a card acquired the week its set came out, where no price exists from
that far back. It is an answer rather than a gap: show it as "no price from then" rather than as
zero movement. Two rarer cases answer `null` the same way, and in both a price does exist on
`since`: a start of exactly `0`, which no percentage can be measured from, and a printing that
carries no current price to compare against.

## Telling a held price from a stale one

`start_as_of` is usually earlier than the `since` you sent. A price carries a point only for the
days it changed, so the day you acquired a card generally has no point of its own, and the last
price before it is the one that stood. Compare the two dates to tell a price that held steady from
one that had not been seen in a while.

## What `listing_count` covers

`listing_count` describes the current price and has no equivalent inside `change`. A
`listing_count` of `1` means a single seller's asking price rather than a market consensus. Treat
a valuation built on those with the same caution you would treat the listing itself.


## OpenAPI

````yaml openapi.json POST /v1/prices/lookup
openapi: 3.0.0
info:
  title: Cromos API
  version: 1.0.0
servers:
  - url: https://api.cromos.so
security:
  - bearerAuth: []
paths:
  /v1/prices/lookup:
    post:
      tags:
        - Prices
      summary: Look up prices in bulk
      description: >-
        Current prices for up to 500 printings in one request, and how far each
        has moved since a date you choose per item. Send `since` on an item to
        get its `change`, and send the same printing more than once to price
        copies you acquired on different days. Answers come back in the order
        you sent them, one per item. More items than the limit is a 400, so a
        partial valuation is never returned as though it were a whole one.
      operationId: lookupPrices
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PriceLookupBody'
      responses:
        '200':
          description: One priced row per item, in the order sent
          headers:
            ETag:
              description: >-
                A strong validator over the response body. Send it back as
                `If-None-Match` to get a `304 Not Modified` when nothing has
                changed.
              schema:
                type: string
            RateLimit:
              description: >-
                Remaining budget in the current window, RFC 9239 draft-7 form:
                `limit=300, remaining=284, reset=41`. `reset` is seconds until
                the window rolls.
              schema:
                type: string
            RateLimit-Policy:
              description: >-
                The policy the budget above is drawn from. `300;w=60` is 300
                requests a minute.
              schema:
                type: string
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      items:
                        type: array
                        items:
                          $ref: '#/components/schemas/PriceLookupRow'
                    required:
                      - items
                  error:
                    type: object
                    nullable: true
                required:
                  - data
                  - error
              example:
                data:
                  items:
                    - card_variant_id: 14
                      card_id: base1-4
                      condition: NM
                      currency: USD
                      market: 85979
                      low: 42650
                      high: 149965
                      listing_count: 13
                      observed_on: '2026-08-18'
                      change:
                        since: '2026-07-31'
                        start_cents: 80042
                        start_as_of: '2026-07-31'
                        change_cents: 5937
                        change_bp: 742
                    - card_variant_id: 14
                      card_id: base1-4
                      condition: NM
                      currency: USD
                      market: 85979
                      low: 42650
                      high: 149965
                      listing_count: 13
                      observed_on: '2026-08-18'
                error: null
        '304':
          description: >-
            Not modified. The `If-None-Match` you sent still matches, and there
            is no body.
          headers:
            ETag:
              description: >-
                A strong validator over the response body. Send it back as
                `If-None-Match` to get a `304 Not Modified` when nothing has
                changed.
              schema:
                type: string
            RateLimit:
              description: >-
                Remaining budget in the current window, RFC 9239 draft-7 form:
                `limit=300, remaining=284, reset=41`. `reset` is seconds until
                the window rolls.
              schema:
                type: string
            RateLimit-Policy:
              description: >-
                The policy the budget above is drawn from. `300;w=60` is 300
                requests a minute.
              schema:
                type: string
        '400':
          description: Bad request
          headers:
            ETag:
              description: >-
                A strong validator over the response body. Send it back as
                `If-None-Match` to get a `304 Not Modified` when nothing has
                changed.
              schema:
                type: string
            RateLimit:
              description: >-
                Remaining budget in the current window, RFC 9239 draft-7 form:
                `limit=300, remaining=284, reset=41`. `reset` is seconds until
                the window rolls.
              schema:
                type: string
            RateLimit-Policy:
              description: >-
                The policy the budget above is drawn from. `300;w=60` is 300
                requests a minute.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          headers:
            ETag:
              description: >-
                A strong validator over the response body. Send it back as
                `If-None-Match` to get a `304 Not Modified` when nothing has
                changed.
              schema:
                type: string
            RateLimit:
              description: >-
                Remaining budget in the current window, RFC 9239 draft-7 form:
                `limit=300, remaining=284, reset=41`. `reset` is seconds until
                the window rolls.
              schema:
                type: string
            RateLimit-Policy:
              description: >-
                The policy the budget above is drawn from. `300;w=60` is 300
                requests a minute.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: Request body larger than 256 KB
          headers:
            ETag:
              description: >-
                A strong validator over the response body. Send it back as
                `If-None-Match` to get a `304 Not Modified` when nothing has
                changed.
              schema:
                type: string
            RateLimit:
              description: >-
                Remaining budget in the current window, RFC 9239 draft-7 form:
                `limit=300, remaining=284, reset=41`. `reset` is seconds until
                the window rolls.
              schema:
                type: string
            RateLimit-Policy:
              description: >-
                The policy the budget above is drawn from. `300;w=60` is 300
                requests a minute.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limited
          headers:
            ETag:
              description: >-
                A strong validator over the response body. Send it back as
                `If-None-Match` to get a `304 Not Modified` when nothing has
                changed.
              schema:
                type: string
            RateLimit:
              description: >-
                Remaining budget in the current window, RFC 9239 draft-7 form:
                `limit=300, remaining=284, reset=41`. `reset` is seconds until
                the window rolls.
              schema:
                type: string
            RateLimit-Policy:
              description: >-
                The policy the budget above is drawn from. `300;w=60` is 300
                requests a minute.
              schema:
                type: string
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal error
          headers:
            ETag:
              description: >-
                A strong validator over the response body. Send it back as
                `If-None-Match` to get a `304 Not Modified` when nothing has
                changed.
              schema:
                type: string
            RateLimit:
              description: >-
                Remaining budget in the current window, RFC 9239 draft-7 form:
                `limit=300, remaining=284, reset=41`. `reset` is seconds until
                the window rolls.
              schema:
                type: string
            RateLimit-Policy:
              description: >-
                The policy the budget above is drawn from. `300;w=60` is 300
                requests a minute.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    PriceLookupBody:
      type: object
      properties:
        items:
          type: array
          items:
            type: object
            properties:
              card_variant_id:
                type: integer
                description: >-
                  Which printing you own. This is the `id` on a card's
                  `variants`, from Get a card. A card id is not enough on its
                  own: one card can carry several printings, and they are priced
                  separately and move independently.
              condition:
                type: string
                enum:
                  - NM
                  - LP
                  - MP
                  - HP
                  - DMG
                default: NM
                description: The condition you own, as a code. Defaults to `NM`.
              since:
                type: string
                format: date
                description: >-
                  The date you acquired this copy, as `YYYY-MM-DD`. Send it to
                  get a `change` object for this item. Each item carries its
                  own, so one request prices a collection where every card was
                  acquired on a different day. Omit it for the current price
                  alone.
            required:
              - card_variant_id
          minItems: 1
          maxItems: 500
          description: >-
            The printings to price, at most 500 per request. Send the same
            printing more than once to price several copies acquired on
            different days: each entry gets its own answer.
      required:
        - items
    PriceLookupRow:
      type: object
      properties:
        card_variant_id:
          type: integer
          description: The printing you asked about, echoed back.
        card_id:
          type: string
          nullable: true
          description: >-
            The card this printing belongs to. `null` when no printing carries
            that id.
        condition:
          type: string
          description: The condition you asked about, echoed back.
        currency:
          type: string
          enum:
            - USD
        market:
          type: integer
          nullable: true
          description: An integer amount in cents (minor units).
        low:
          type: integer
          nullable: true
          description: An integer amount in cents (minor units).
        high:
          type: integer
          nullable: true
          description: An integer amount in cents (minor units).
        listing_count:
          type: integer
          nullable: true
          description: >-
            How many active listings this price is computed from. 1 means the
            price is a single seller's asking price, not a market consensus. The
            value caps at 25, so 25 means 25 or more. Null where no count is
            available.
        observed_on:
          type: string
          nullable: true
          description: >-
            The date this price was observed, as YYYY-MM-DD. `null` where this
            printing carries no price.
        change:
          type: object
          nullable: true
          properties:
            since:
              type: string
              description: The date you sent for this item, echoed back.
            start_cents:
              type: integer
              description: >-
                The market price that stood on `since`, in cents. Not
                necessarily a price observed that day: see `start_as_of` for
                when it was last seen.
            start_as_of:
              type: string
              description: >-
                The day `start_cents` was actually observed. Earlier than
                `since` whenever the price had not moved for a while: a price
                carries a point only for days it changed, so the day you
                acquired a card usually has no point of its own.
            change_cents:
              type: integer
              description: '`market - start_cents`.'
            change_bp:
              type: integer
              description: >-
                Change in basis points. `+12.93%` is `1293`. Integer, never a
                float.
          required:
            - since
            - start_cents
            - start_as_of
            - change_cents
            - change_bp
          description: >-
            Absent unless the item sent `since`. `null` when it did and no
            change can be worked out, which is an answer rather than a gap.
            Almost always that means the printing has no price at or before that
            date; it also covers a start of exactly `0`, which no percentage can
            be measured from, and a printing carrying no current price.
      required:
        - card_variant_id
        - card_id
        - condition
        - currency
        - market
        - low
        - high
        - listing_count
        - observed_on
    ErrorResponse:
      type: object
      properties:
        data:
          type: object
          nullable: true
        error:
          $ref: '#/components/schemas/ApiError'
      required:
        - data
        - error
    ApiError:
      type: object
      properties:
        code:
          type: string
          description: A stable machine-readable token. Switch on this, not on `message`.
        message:
          type: string
          description: A human-readable sentence. Always present, never empty.
      required:
        - code
        - message
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer

````