Hammerdown

Partner API

Read your auctions and bids, and place bids from your own systems, over HTTP. Available to stores on the Pro plan.

Authentication

Every request carries an API key as a bearer token. Generate one in the app's Developers tab; the key is shown once at creation and only its hash is stored, so if you lose it you issue a new one rather than recovering the old.

Request

curl https://hammerdown.app/api/auctions \
  -H "Authorization: Bearer hmd_your_key_here"

A key belongs to one store and can only ever reach that store's data. Entitlement is checked on every request rather than at issue time, so a key stops working the moment its store drops below Pro or uninstalls the app. Revoking a key in the Developers tab takes effect immediately.

Money, time, and ids

Every amount is an integer in the currency's minor units, alongside the auction's currency code. A bid of $125.00 on a USD auction is 12500. This keeps amounts exact and locale-free; format them with the currency's own exponent. Timestamps are ISO 8601 strings in UTC, and anything not yet determined is null rather than absent. Customer, product, and variant ids are Shopify's, as strings.

Header names are written here in their conventional casing. HTTP treats them case-insensitively, and most clients hand them to you lower-cased.

Pagination

Lists are keyset paginated and ordered newest first. Each response carries a next_cursor; pass it back as before to get the following page, and stop when it comes back null. Unlike an offset, a cursor does not skip or repeat rows when new auctions arrive mid-walk.

Response

{
  "data": [ ... ],
  "next_cursor": 4182
}

Errors

Any non-2xx response carries a single error object. Branch on code, which is stable; treat message as human-readable text that may be reworded.

Response

{
  "error": {
    "code": "invalid_bid",
    "message": "A bid needs customer_id, a positive whole-number amount, and an idempotency_key."
  }
}

Codes

Name Status Description
unauthorized 401 The Authorization header is missing, malformed, or unknown.
forbidden 403 The store's plan no longer includes API access.
api_bidding_disabled 403 The store has not opted in to bidding over the API.
not_found 404 No auction with that id belongs to this store.
invalid_bid 422 A bid was missing a field or carried a malformed amount.
(rejection reason) 422 The auction refused the bid, for example because it is not live, the amount is below the next valid bid, or the bidder is not invited.
rate_limited 429 The key is over its request budget for the current window.

Rate limits

Two budgets apply to every request. Each key gets 90 requests per 60 seconds, and your store gets 150 requests per 60 seconds across all of its keys together. Whichever runs out first is the one that stops you.

Issuing more keys therefore separates your integrations, it does not buy throughput, and a store may hold 10 keys at a time. Split them by system so a busy one cannot exhaust another's budget, and so you can revoke one without disturbing the rest.

Headers

Name Present on Description
X-RateLimit-Limit every response Requests allowed in a window.
X-RateLimit-Remaining every response Requests left in the current window.
X-RateLimit-Reset every response Unix timestamp in seconds when the window resets.
Retry-After 429 only Seconds to wait before retrying.

The headers describe whichever of the two budgets has least left, so pacing against them respects both.

Over the limit the request is rejected with 429 and the code rate_limited. Wait out Retry-After rather than retrying immediately, since a rejected request still counts as traffic.

Endpoints

GET /api/auctions

Your store's auctions, newest first.

Query parameters

Name Type Description
status string Return only auctions in this state: scheduled, live, ended, settled, canceled. The extra value awaiting_payment selects auctions that have a winner who has not paid.
limit integer Page size. Defaults to 50, values above 250 are capped there.
before integer Continue after a previous page: pass the next_cursor it returned. Omit for the first page.

Request

curl "https://hammerdown.app/api/auctions?status=live&limit=2" \
  -H "Authorization: Bearer hmd_your_key_here"

Response

{
  "data": [
    {
      "id": 4182,
      "title": "Signed first pressing",
      "status": "live",
      "currency": "USD",
      "product_id": "gid://shopify/Product/8899",
      "variant_id": "gid://shopify/ProductVariant/4471",
      "product_title": "Signed first pressing",
      "variant_title": null,
      "product_handle": "signed-first-pressing",
      "starting_price": 5000,
      "reserve_price": 20000,
      "min_increment": 500,
      "buyout_price": null,
      "proxy_enabled": true,
      "high_bid": 12500,
      "high_bidder_id": "9920775979235",
      "bid_count": 8,
      "winner_customer_id": null,
      "starts_at": "2026-08-24T15:00:00.000000Z",
      "ends_at": "2026-08-26T15:00:00.000000Z",
      "settled_at": null,
      "paid_at": null,
      "canceled_at": null,
      "created_at": "2026-08-20T11:04:22.914213Z",
      "updated_at": "2026-08-25T09:31:07.552019Z"
    }
  ],
  "next_cursor": 4182
}
GET /api/auctions/:id

One auction. Responds 404 not_found for an id that does not exist or belongs to another store, so an id from elsewhere cannot be probed for existence.

Response

{
  "data": { "id": 4182, "status": "live", ... }
}
GET /api/auctions/:auction_id/bids

The bids standing on an auction, newest first. Private proxy maximums are never returned; a bid appears here at the amount it was actually placed for.

Query parameters

Name Type Description
limit integer Page size. Defaults to 50, values above 250 are capped there.
before integer Continue after a previous page: pass the next_cursor it returned. Omit for the first page.

Response

{
  "data": [
    {
      "id": 91044,
      "auction_id": 4182,
      "customer_id": "9920775979235",
      "amount": 12500,
      "origin": "proxy",
      "placed_at": "2026-08-25T09:31:07.114522Z"
    }
  ],
  "next_cursor": null
}
POST /api/auctions/:auction_id/bids

Places a bid on behalf of a customer your own system has authenticated. The bid runs through the same auction process and guards as a storefront bid, so anti-snipe extensions, reserves, buyouts, and guest lists all apply.

This endpoint is off until the store opts in, under "Bidding over the API" in the Developers tab, because it is a trust decision rather than a technical one: the key proves the store, and the store vouches for the customer_id it sends. Until it is enabled the endpoint answers 403 api_bidding_disabled.

Body

Name Type Description
customer_id string The Shopify customer the bid is for. Your backend is trusted to have authenticated them.
amount integer The bid, in the currency's minor units. Must be a positive whole number.
idempotency_key string Your own unique string for this bid. Replaying it will not place a second bid.

Request

curl -X POST https://hammerdown.app/api/auctions/4182/bids \
  -H "Authorization: Bearer hmd_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"customer_id": "9920775979235", "amount": 13000, "idempotency_key": "a3f1-9c22"}'

Response

Name Type Description
status string The auction's state after the bid.
high_bid integer The leading amount, in minor units.
bid_count integer Bids standing on the auction.
ends_at string When bidding closes, which a bid may have extended.
you_are_winning boolean Whether the customer you bid for now leads.

Response

{
  "data": {
    "status": "live",
    "high_bid": 13000,
    "bid_count": 9,
    "ends_at": "2026-08-26T15:02:00.000000Z",
    "you_are_winning": true
  }
}

A rejected bid returns 422 with the reason as its code, for example when the auction is not live, the amount is below the next valid bid, or the bidder is not on an invite-only auction's guest list.

The auction object

The same shape is returned by the read endpoints and carried in webhook payloads, so a consumer of one already understands the other.

Fields

Name Type Description
id integer The auction's identifier.
title string The auction's name, or null to fall back to the product title.
status string scheduled, live, ended, settled, canceled
currency string ISO currency code every amount on the auction is expressed in.
product_id, variant_id string The Shopify product and variant being auctioned.
product_title, variant_title, product_handle string The catalog copy, denormalized so a consumer needs no second call to Shopify.
starting_price integer The lowest a first bid may be, in minor units.
reserve_price integer The floor below which the auction ends with no winner, or null.
min_increment integer How much each bid must beat the last by.
buyout_price integer The price that ends the auction on the spot, or null.
proxy_enabled boolean Whether bidders may set a private maximum.
high_bid integer The leading amount, or null before the first bid.
high_bidder_id string The customer currently leading, or null.
bid_count integer Bids standing on the auction.
winner_customer_id string Set once the auction ends with a winner.
starts_at, ends_at string When bidding opens and closes.
settled_at string When the winner's draft order was created.
paid_at string When the winner paid.
canceled_at string When the auction was canceled, if it was.
created_at, updated_at string Record timestamps.

The bid object

Fields

Name Type Description
id integer The bid's identifier, and the cursor value for pagination.
auction_id integer The auction the bid was placed on.
customer_id string The Shopify customer who bid.
amount integer The bid, in the auction's minor units.
origin string How it arrived: customer from the storefront, proxy from an automatic bid, api from this API.
placed_at string When the bid was accepted.

Getting told when something changes

Polling this API is fine for a nightly reconciliation, but for reacting to a bid or a win you want webhooks, which push a signed payload to your endpoint as it happens.