---
title: "Resolve a name to one firearm"
method: GET
path: "/v1/firearms/resolve"
tags: ["Firearms"]
---

# Resolve a name to one firearm

`GET /v1/firearms/resolve`

## Request

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

Turns a name as somebody actually writes it ("G19 gen 5 MOS", "AK-47", "H&K MP5") into a single catalog id, or says why it cannot.

This is not search. `/v1/firearms/search` ranks a page of records about a query; this answers which record a query *is*, and reports its own certainty. It is a deterministic lookup over an index of spellings, so the same query returns the same id indefinitely, and it is the endpoint to call before any other when you are starting from text rather than from an id.

Three things about the response are deliberate. An ambiguous query is not decided for you: "Glock 19" is three generations, so `status` is `ambiguous`, `firearmId` is null, and all three are returned to choose between. Words the resolver could not place are returned in `unresolvedTokens` rather than dropped: "MOS" is a variant the catalog does not hold, and knowing that is worth more than being handed the base model as though it were the answer. And a `fuzzy` candidate is a suggestion offered when nothing matched at all; it never appears as a resolution.

**Requires Builder tier or higher.**

## Query parameters

- `q` string, required

## Headers

- `If-None-Match` string

## Response `200`

The resolution, whether or not it resolved. A query that matches nothing is a 200 with `status: "not_found"`, not a 404, because the request was answerable and the answer is that we hold no such record.

- object
  - `success` boolean, required: Always true on a successful response. Every error carries `false` and an `error` object instead.
  - `data` object, required: What one query resolved to.
    - `query` string: The query this result answers, echoed back so a batch response can be matched to its request.
    - `status` string: One of `resolved` (exactly one record), `ambiguous` (several equally good, none chosen) or `not_found`.
    - `firearmId` string, nullable: The resolved catalog id. Null on `ambiguous` and `not_found`, since an ambiguous query is deliberately not decided for you.
    - `match` string, nullable: How the resolution was made, or null when nothing resolved. Never `fuzzy`: a suggestion is not a resolution.
    - `score` number: Confidence in the resolution, 0 to 1. Derived from how much of the query a catalogued spelling accounted for and how strong that spelling is as evidence.
    - `matchedTokens` string[]: The words of the query a catalogued spelling accounted for.
    - `unresolvedTokens` string[]: The words it did not. Reported rather than guessed at: a variant we do not hold appears here instead of being silently dropped.
    - `alternatives` object[]: Other records this query could mean, best first. On `ambiguous` these are the tied candidates and choosing between them is yours.
      - `firearmId` string: The catalog id of this candidate.
      - `name` string: The catalogued name of this candidate.
      - `manufacturerName` string, nullable: The maker, or null where the record has none.
      - `match` string: How this candidate was matched. One of `slug`, `name`, `alternate`, `shorthand`, `base` (the name with its maker removed), `family` (the model designation with its trailing descriptor removed, e.g. `M4` from `M4 Carbine`) or `fuzzy`.
      - `matchedAlias` string, nullable: The catalogued spelling that matched, or null for a fuzzy suggestion.
      - `score` number: Confidence in this candidate, 0 to 1. Always 0 for a fuzzy suggestion, which is offered rather than scored.

## Other responses

- `304`: Your copy is current. No body is sent, and this response does not count toward the daily request allowance.
- `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.
- `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)
