---
title: "List firearms an attachment fits"
method: GET
path: "/v1/attachments/{id}/firearms"
tags: ["Compatibility"]
---

# List firearms an attachment fits

`GET /v1/attachments/{id}/firearms`

## Request

- Base URL: `https://api.gunspec.io`
- URL: `https://api.gunspec.io/v1/attachments/{id}/firearms`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

The reverse lookup: every catalogued firearm the attachment fits, with fit type, confidence and reason. Candidates come from the interface index and are confirmed by the same engine as the forward endpoint, so the two never disagree. `meta.universal` is true for an item with no requirements and no ratings (a sling), in which case the whole catalog is listed; `meta.truncated` is true when the candidate set hit its cap.

**Requires Studio tier or higher.**

## Path parameters

- `id` string, required

## Query parameters

- `page` integer
- `per_page` integer

## Response `200`

List firearms an attachment fits

- object
  - `success` boolean, required: Always true on a successful response. Every error carries `false` and an `error` object instead.
  - `data` object[], required: One page of results. Use `pagination` to walk the rest.
    - `id` string: Slug of the firearm the attachment fits.
    - `name` string: Display name, as the maker writes it.
    - `fitType` 'direct' | 'adapter' | 'curated': How a fit was reached: on the firearm's own interface, through one adapter, or by a curated override.
    - `source` 'curated' | 'universal' | 'inferred' | 'inherited:parent' | 'inherited:platform': The weakest evidence behind a fit. A retailer or engineering consumer should treat `inferred` as unverified; `min_confidence` hides it.
    - `confidence` number: How well evidenced this row is, 0 to 1. **A minimum, not an average**: an inferred row caps every fit computed through it, and `min_confidence` on the endpoint hides rows below a threshold.
    - `reason` string: Plain-language account of why it fits, safe to show in a UI.
    - `via` FitVia[]: The interfaces that satisfied the attachment requirements, with where each came from.
      - `position` string: Where on the firearm the interface sits: `muzzle`, `rear`, `top`, `bottom`.
      - `standardId` string: Id of the interface standard, kind-prefixed, e.g. `thread:1/2x28`. Ids are never renamed; new spellings become aliases.
      - `source` 'curated' | 'inferred' | 'inherited:parent' | 'inherited:platform': Where an interface row on a firearm came from. `curated` is a person; `inferred` is the backfill, capped at 0.6 confidence; the inherited kinds came through the parent chain or a platform.
      - `adapterId` string, nullable: The adapter that bridges this fit, or null when none is needed. Threads, magazine wells and the caliber gate are never bridged.
    - `adapter` AdapterRef
      - `id` string: Slug of the adapter part.
      - `name` string: Display name, as the maker writes it.
    - `manufacturer` ManufacturerRef
      - `id` string: Slug of the manufacturer.
      - `name` string: Display name, as the maker writes it.
    - `category` string, nullable: Display name of the firearm category.
    - `images` InlineMediaItem[]: Renders and photographs of the firearm, ordered so `images[0]` is the frame to show at rest.
      - `id` integer, required: Autoincrementing image id.
      - `url` string, required: Absolute, directly fetchable URL.
      - `kind` 'silhouette' | 'render' | 'photo' | 'schematic' | 'model', required: What kind of asset a media row is.
      - `alt` string, nullable: Alt text, where one has been written. Null otherwise, so do not render an empty string.
      - `width` integer, nullable: Pixel width, where it is known.
      - `height` integer, nullable: Pixel height, where it is known.
    - `svgLineArtUrl` string, nullable: Line-art silhouette, or null where none has been drawn.
    - `yearIntroduced` integer: Year it entered production or service. Null where the date is disputed or unknown.
  - `pagination` Pagination: Page-size is reported under both `per_page` (matching the request parameter) and `limit` (retained for existing integrations). They always carry the same value. `total` and `totalPages` are omitted for Explorer and anonymous callers.
    - `page` integer, required: The page this response is, 1-based.
    - `per_page` integer, required: Page size. Same value as `limit`.
    - `limit` integer, required: Page size. Alias of `per_page`.
    - `total` integer: Total records matching the query across every page.
    - `totalPages` integer: How many pages the result set spans at the current `per_page`.

## Other responses

- `400`: Validation error
- `401`: Missing or invalid API key
- `403`: The credential is valid and the caller is not permitted. Three causes, none of which reissuing a key changes: the plan is below the endpoint's tier, the account is suspended, or the key is not attached to the thing being acted on, meaning a user account for `/v1/me` or a shop for `/v1/vendor`. Distinct from 401, which means the credential is missing, malformed or expired and a new one can work.
- `404`: Resource not found
- `429`: A limit was reached. `RATE_LIMITED` is the per-minute limit: wait `Retry-After` seconds (a minute) and retry. `DAILY_CAP_EXCEEDED` is the plan's daily limit, per key: nothing will be served until the counters return to zero at midnight UTC. `MONTHLY_CAP_EXCEEDED` is the plan's monthly allowance, for the whole account: nothing will be served until midnight UTC on the first of next month, or until the plan changes. For both, `Retry-After` is the seconds until then and `error.resetsAt` says when as an instant. Calls made after a refusal are refused too and do not spend the month, and a free key that keeps calling is paused, so stop at the first one and resume at the reset.
- `500`: Internal server error

---

[API](https://skmtc.dev/gunspec/apis/gunspec-io-api.md) · [All operations](https://skmtc.dev/gunspec/apis/gunspec-io-api/llms.txt) · [OpenAPI document](https://skmtc.dev/gunspec/apis/gunspec-io-api/revisions/f2e974d8b05c?raw)
