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
/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
}
/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", ... }
}
/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
}
/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.