GunSpec

Seller

A shop reaches these endpoints in four steps, and the third is the one nothing else explains.

  1. Hold an Enterprise plan. Listings are a plan perk. Nothing in these tables bills anyone and there is no separate advertising product.
  2. Apply, and be approved by a person. Apply at https://gunspec.io/en/sellers. Approval creates the shop; until then nothing of yours is public.
  3. Name a key. There is no such thing as a "vendor key" and no scope column on keys. You create an ordinary API key, then name it on the shop in Profile > Seller > Shop settings > Pushing from a server. That mapping is the entire vendor scope: the shop says which key it accepts a push from, and your key value is never sent to us. Several shops may name the same key; when they do, every call below needs ?vendor=, and GET /v1/vendor/shops lists the ids that key may act for.
  4. Read, then write. GET /v1/vendor/offers for what we hold, PATCH for a price or stock change, PUT for a bulk upsert, DELETE to withdraw. Read first and a nightly sync sends the rows that changed rather than all of them.

Four rules the API enforces and a retailer usually meets late:

  • Money is integer minor units plus an ISO 4217 code. 139900 with AUD is A$1,399.00. A float is refused at the boundary rather than rounded quietly. Minor units are not always hundredths: JPY has none, KWD has three. Send the units of the currency you name.
  • `stock_qty` is private and never appears in a public response. 0 renders as "No stock at this supplier", any other number as in stock, null as not tracked. A live count is competitive information, so we store it and never publish it. If you do not count units, send in_stock instead: a row that carries a quantity derives the flag from it, so the two cannot disagree.
  • Writing is also a claim that the price still stands. An offer nobody has refreshed in 30 days from a feed (90 from the console) stops appearing, because a stale price is worse than none. Re-pushing an unchanged row is how you say nothing changed.
  • A mapping error is a review, not a rejected batch. SKUs naming an id we do not hold come back in unmatched. A file of 400 rows is not refused because three ids were wrong.

GunSpec never takes the order. An offer carries a URL and links out; there is no basket, payment or customer anywhere in this surface. Public rows link through /v1/out/{clickId}, which counts the visit and redirects to you, so the traffic we send is measurable - read it in Profile > Seller > Traffic. region on an offer hides sellers who do not ship somewhere: a display convenience, never a statement about what is lawful to own.

Authentifizierung erforderlichExplorer+

Who stocks this firearm and where to buy it. The same three guards as an attachment's offers apply: only approved vendors appear, an offer not refreshed within 30 days is hidden, and region hides sellers who do not ship there. Offers link out; GunSpec never takes the order.

GET/v1/firearms/{id}/offers

  • stringHeadererforderlich

    Ihr API-Schlüssel. Authorization: Bearer <key> wird ebenfalls akzeptiert.

  • stringerforderlich

    Firearm slug

    Akzeptiert
    a lowercase slug: letters, digits and hyphens
    Beispiel

  • string

    ISO 3166-1 alpha-2 code; hides sellers who do not ship there

    Beispiel

  • PublicOffer[]

    The response payload.

  • object

    The shop stocking the part.

  • string

    Slug of the shop.

  • string

    Display name, as the maker writes it.

  • string | null

    Official website, where one is known.

  • string | null

    Absolute URL of the logo, or null where none is on file.

  • string

    The shop's own catalogue key for the listing. A seller's identifier, not ours: it is what PATCH /v1/vendor/offers/{sku} addresses.

  • integer

    Price in integer minor units of currency, so cents for AUD. Never a float. Format with Intl.NumberFormat at render rather than dividing first.

  • string

    ISO 4217 code the price is in, e.g. AUD. Prices are integer minor units; format with Intl.NumberFormat at render rather than dividing first.

  • string

    Absolute, directly fetchable URL.

  • boolean

    Whether the shop reports it in stock. Derived from stockQty where the shop tracks a count.

  • string | null

    Opaque token for the outbound link: send readers to /v1/out/{clickId} so the visit is counted for the seller. Null on a listing that cannot currently be followed.

  • string | null

    ISO 3166-1 alpha-2 code the seller ships to. Null means they state no restriction.

  • string

    When the seller last refreshed this listing. A listing not refreshed inside its freshness window is hidden rather than shown stale.

  • 400VALIDATION_ERRORValidation error
  • 401UNAUTHORIZEDAPI key required
  • 403FORBIDDENValid key, not permitted
  • 404NOT_FOUNDResource not found
  • 429RATE_LIMITEDRate limit exceeded
  • 500INTERNAL_ERRORUnexpected server error

Mindeststufe
Explorer+
Monatskontingent
200/mo
Anfragerate
10/min
Tageslimit
50

Die Werte gelten für die Mindeststufe dieses Endpunkts. Höhere Stufen erhöhen alle Limits.