GunSpec

Compatibility

Fit is computed from mount interfaces, never from names. A firearm declares what it exposes at each position (muzzle thread, rails, optic cut, stock and grip interfaces, magazine well); an attachment declares what it requires; the engine matches the two, through a named adapter where one bridges them. A caliber, bore or minimum-barrel mismatch is never bridged and never inferred.

Every fit carries `source` and `confidence`, and they are the first two fields to read. source is the weakest evidence in the chain that produced the fit; confidence is capped by the weakest interface it went through. The values, strongest first:

  • curated: a person recorded this interface or this fit against the maker's page.
  • universal: the attachment requires nothing (a sling), so every firearm fits it.
  • inherited:parent: the interface came from the firearm's parent record.
  • inherited:platform: the interface came from a platform the firearm belongs to (an AK-100 default, a mil-spec AR-15 default).
  • inferred: the interface was derived by a script from the record's own fields, at 0.6 or below. Convention rows are the weakest inferred evidence and are labelled as such: where a firearm is tagged threaded or optics-ready but names no standard, the backfill applies industry convention (a thread by cartridge and country, an optic plate by the maker's system) at 0.5 to 0.6, with a convention: note on the interface row.

A retailer or an engineering consumer should treat `inferred`, and anything below a confidence they choose, as unverified. Pass min_confidence to hide fits computed through interfaces below that figure; nothing in a response presents an inferred or convention row as verified, and neither should a product page built on it. via[] names each interface the fit went through with its own source, so a fit can be audited hop by hop; GET /v1/firearms/{id}/interfaces lists every row with its source, confidence and inheritedFrom.

Browsing the catalog (/v1/attachments, /v1/attachments/{id}, /v1/interfaces) is open to any key. Everything that answers "does this go on that" is Studio, and a lower plan receives 403 SUBSCRIPTION_REQUIRED rather than an empty list, so never read an error as "nothing fits". fits= on the attachment list is the same computation wearing a query parameter and is gated the same way.

Auth RequiredExplorer+

The attachment catalog: stocks, grips, handguards, optics, mounts, muzzle devices, suppressors, magazines, lights, bipods, slings, triggers and adapters. requires=<standard> narrows to what needs a given interface. Browsing is open to any key. fits=<firearm> runs the compatibility engine and therefore needs Studio, the same as the dedicated fit endpoints.

GET/v1/attachments

  • stringheaderrequired

    Your API key. Authorization: Bearer <key> is accepted in its place.

  • integerdefault1

    Page number, from 1 to 10,000

    Range
    1 – 10000
    Example
  • integerdefault20

    Items per page (max 100)

    Range
    1 – 100
    Example
  • string

    Attachment category

    Max length
    100
    Example
  • string

    Manufacturer slug

    Max length
    100
    Example
  • string

    Only attachments that fit this firearm

    Max length
    100
    Example
  • string

    Only attachments requiring this interface standard

    Max length
    80
    Example
  • string

    Substring match on name

    Max length
    60
    Example
  • boolean

    Only factory parts (true) or only aftermarket (false)

  • string

    Production status. The catalog keeps discontinued parts on purpose, so a store front asks for in_production

    Accepts
    Example
  • string

    Restrict to one seller. me resolves to the vendor your key belongs to

    Max length
    64
    Example
  • boolean

    Drop anything that seller does not currently stock. Needs a resolvable vendor, and yields nothing without one

  • Attachment[]

    One page of results

  • string

    URL-safe slug identifying the part, e.g. magpul-zhukov-s-stock.

  • string

    Display name, as the maker writes it.

  • string

    What kind of part it is: stock, optic, muzzle, magazine, grip.

  • ManufacturerRef | null

    Who makes it.

  • string | null

    The maker's own SKU, where they publish one.

  • number | null

    Weight in grams.

  • number | null

    Length in millimetres.

  • integer | null

    Magazines only

  • number | null

    Suppressors and muzzle devices

  • string | null

    The mount position this part occupies, or null when it takes none.

  • boolean

    True when the part ships on the firearm from the factory. A factory part carries no statMods or specDeltas, because the firearm's own figures already include it.

  • string | null

    The firearm this is the factory part for, when isFactoryPart is true.

  • object

    Editorial adjustments to the game statistics once fitted, keyed by stat. Same status as game_* on a firearm: not measured.

  • object

    Specification changes once fitted, keyed by field: a folding stock changes the folded length, for example.

  • string | null

    Absolute URL of the manufacturer product photo on the assets CDN, ready to render. Null when no photo has been sourced yet.

  • AttachmentStatus

    Production status of an attachment. limited corresponds to a firearm's limited_production.

  • number | null

    How completely the record is specified and how well it is sourced, 0 to 1. A record-level completeness and provenance measure, not a per-field probability of correctness. Use it to rank and triage; use sources to verify an individual number.

  • string

    When a field we serve last changed. Maintained by the database rather than by whatever wrote the row, so it moves for a corrected specification and does not move for a re-run of the importer.

  • string | null

    Opaque fingerprint of this record. It changes when and only when a field we serve changes, so a client holding the same value already has the record and need not fetch it. Equality is the only supported operation: do not parse it, and do not assume how it is computed. Null means the record has not been written since versioning began, not that it is unchanged.

  • string[]

    Standard ids this adds to the firearm once fitted. Resolved to { id, name, kind } rows on /v1/attachments/{id}.

  • number

    Current page number

  • number

    Items per page

  • number

    Total matching records (Builder and above)

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

Minimum tier
Explorer+
Monthly quota
200/mo
Rate limit
10/min
Daily cap
50

Figures shown are for this endpoint's minimum tier. Higher tiers raise every limit.

This read answers conditional requests. Keep the ETag from a response and send it back as If-None-Match: an unchanged record returns 304 Not Modified with no body, which counts toward your per-minute rate limit but not your daily allowance.