# Calibers

Resource class for interacting with the GunSpec Calibers API. Wraps all `/v1/calibers` endpoints. Instantiated internally by the GunSpec client and exposed as `client.calibers`.

Source: https://docs.gunspec.io/en/sdk/calibers

## client.calibers.list()

`GET /v1/calibers`

Auth: API key required  
Tier: Explorer+

List calibers with optional filters and pagination.

### Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `page` | integer | no | Page number, from 1 to 10,000 |
| `per_page` | integer | no | Items per page (max 100) |
| `q` | string | no | Substring match on caliber name or alias |
| `cartridge_type` | string | no | Filter by cartridge type |
| `primer_type` | string | no | Filter by primer type |
| `action_type` | string | no | Only cartridges chambered by at least one firearm with this action, as stored and underscored. The vocabulary is /v1/firearms/action-types |
| `If-None-Match` | string | no | The `ETag` from a previous response. If the body would be byte-identical, the API answers `304 Not Modified` with no body. A 304 counts toward the per-minute rate limit but not the daily request allowance. |

### Response 200 (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `data` | Caliber[] | One page of results |
| `data[].id` | string | URL-safe slug identifying the cartridge, e.g. `9x19mm-parabellum`. |
| `data[].name` | string | Display name, as the maker writes it. |
| `data[].aliases` | string[] | Other names this cartridge is sold or written as. |
| `data[].natoDesignation` | string \| null | NATO designation where the cartridge has one, e.g. `9mm NATO`. Null otherwise. |
| `data[].bulletDiameterMm` | number \| null | Bullet diameter in millimetres. |
| `data[].neckDiameterMm` | number \| null | Case neck outside diameter in millimetres. |
| `data[].shoulderDiameterMm` | number \| null | Bottleneck cases only. |
| `data[].baseDiameterMm` | number \| null | Case base diameter in millimetres, ahead of the extractor groove. |
| `data[].rimDiameterMm` | number \| null | Rim diameter in millimetres. |
| `data[].rimThicknessMm` | number \| null | Rim thickness in millimetres. |
| `data[].caseLengthMm` | number \| null | Case length in millimetres. |
| `data[].overallLengthMm` | number \| null | Overall length in millimetres, stock extended where it folds or collapses. |
| `data[].bulletLengthMm` | number \| null | Typical projectile length. |
| `data[].maxPressureMpa` | number \| null | Maximum average pressure in megapascals, as the standards body specifies it. |
| `data[].maxPressurePsi` | number \| null | The same pressure in pounds per square inch, converted. |
| `data[].typicalBulletWeightG` | number \| null | Representative bullet weight in grams. Divide by 0.0648 for grains. |
| `data[].typicalMuzzleVelocityMps` | number \| null | Representative muzzle velocity in metres per second. |
| `data[].typicalMuzzleEnergyJ` | number \| null | Representative muzzle energy in joules. |
| `data[].primerType` | string \| null | Primer size and type, e.g. `small_pistol`, `large_rifle`, `rimfire`. |
| `data[].cartridgeType` | string \| null | Head type: `rimmed`, `rimless`, `semi_rimmed`, `rebated_rim`, `belted`, plus historical values like `cap_and_ball`. **Not the broad class.** The registry said `pistol`/`rifle`/`shotgun` until the column was read, and it holds neither. |
| `data[].caseShape` | string \| null | Case profile: `straight`, `bottleneck`, `tapered`, `belted`. |
| `data[].caseMaterial` | string \| null | What the case is made of: `brass`, `steel`, `aluminium`, `polymer`. |
| `data[].projectileKind` | string \| null | `none` is a blank. |
| `data[].bulletProfile` | string \| null | Bullet shape: `fmj`, `hollow_point`, `soft_point`, `spitzer`, `round_nose`. |
| `data[].closure` | string \| null | What seals the case mouth; a blank is a star crimp. |
| `data[].markingColor` | string \| null | Paint that identifies the load: crimp lacquer on a blank, tip colour on a military round. |
| `data[].markingMeaning` | string \| null | What that colour signifies, where the marking is a standard one. |
| `data[].parentCartridgeId` | string \| null | The cartridge this one was derived from, or null. A wildcat and its parent case, for example. |
| `data[].yearIntroduced` | integer \| null | Year it entered production or service. Null where the date is disputed or unknown. |
| `data[].designer` | string \| null | Person or team credited with the design, where it is known. |
| `data[].specSource` | string \| null | Page the dimensions were read from. |
| `data[].specStandard` | string \| null | The standards body whose figures these are (`SAAMI`, `CIP`), where the source names one. |
| `data[].dataConfidence` | 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. |
| `data[].verifiedAt` | string \| null | When a person last checked this record against its source. Null means nobody has: the row is model knowledge until then. |
| `data[].verifiedFields` | string[] | The fields the source stated. The same list is in `provenance.verifiedFields`. |
| `data[].createdAt` | string | When the record was first added, `YYYY-MM-DD HH:MM:SS` in UTC. |
| `data[].updatedAt` | 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. |
| `data[].version` | 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. |
| `data[].provenance` | Provenance | Where a record's figures came from and how far they have been checked, in one place. `sources` are the pages consulted; check a specific figure against those. `sourceKinds` says what each page is and `bestSourceKind` the strongest of them, on the hierarchy `SourceKind` defines: a maker or standards-body page is evidence for a figure, a retailer or forum page is evidence the item exists. `dataConfidence` is the 0 to 1 score set from what was actually sourced and never raised by hand. `verifiedAt` and `verifiedFields` say when a source was last read against the record and which fields it stated; null means the row is still seed knowledge. `updatedAt` and `version` are the same cache signals the record carries at the top level. |
| `data[].provenance.sources` | string[] | The pages consulted when the record was compiled, as an array. Check a specific figure against these rather than against `dataConfidence`. |
| `data[].provenance.sourceKinds` | object[] | What each cited page is, in `sources` order, on the `SourceKind` hierarchy. A host not in the source map is `other`, never guessed. |
| `data[].provenance.bestSourceKind` | SourceKind \| null | The strongest kind among the citations, or null when nothing is cited. `manufacturer` or `standards_body` means a figure can be checked against an authority; `retailer` or `community` alone means the record is still supported by copies of copies. |
| `data[].provenance.makerSource` | string \| null | The first cited page on the record's own manufacturer's website, or null when it cites none. A page on another maker's site does not count. Cited from the maker is not the same as checked against it: `verifiedFields` names the figures that were read against a source. |
| `data[].provenance.evidence` | string | How far the record's figures are backed, strongest first, for a badge: `verified` when at least one figure was read against a source (`verifiedFields`), `maker` when it cites a page on its own manufacturer's site (`makerSource`), `authority` when it cites a maker, standards body or government page, `secondary` when it cites only press, retailers, references or forums, `none` when it cites nothing. Derived from the fields beside it. |
| `data[].provenance.checks` | object | The checks the record has been through, on a single record's response only: every task the data agent (Redback) or a person was given about it, what each proposed, the pages it read, and whether a person accepted it. Nothing an agent proposes reaches the record until a person accepts it, so this is the trail behind `verifiedFields`. Who reviewed is not published; an agent's instance name is. |
| `data[].provenance.dataConfidence` | number \| null | The 0 to 1 score set from what was actually sourced, never raised by hand. See the confidence bands in the docs. |
| `data[].provenance.verifiedAt` | string \| null | When a source was last read against this record. Null means the row is still seed knowledge. |
| `data[].provenance.verifiedFields` | string[] | Which fields the source stated, as an array. Everything else on a verified row is still unverified. Null where no source has been read. |
| `data[].provenance.specSource` | string \| null | Cartridges only: the page the drawing figures were taken from. Null on every other kind of record. |
| `data[].provenance.updatedAt` | string | When a served column last changed; the same value the record carries at the top level. |
| `data[].provenance.version` | string \| null | The record's content version, equal to the top-level `version`; equal versions mean equal data. |
| `pagination.page` | number | Current page number |
| `pagination.per_page` | number | Items per page |
| `pagination.total` | number | Total matching records (Builder and above) |

```json
{
  "success": true,
  "data": [
    {
      "id": "9x19mm-parabellum",
      "name": "9x19mm Parabellum",
      "aliases": [
        "7.62x51mm NATO",
        ".308 Win"
      ],
      "natoDesignation": "9mm NATO",
      "bulletDiameterMm": 9.01,
      "neckDiameterMm": 9.65,
      "shoulderDiameterMm": null,
      "baseDiameterMm": 9.93,
      "rimDiameterMm": 9.96,
      "rimThicknessMm": 1.27,
      "caseLengthMm": 19.15,
      "overallLengthMm": 29.69,
      "bulletLengthMm": 15.5,
      "maxPressureMpa": 235,
      "maxPressurePsi": 34084,
      "typicalBulletWeightG": 7.45,
      "typicalMuzzleVelocityMps": 360,
      "typicalMuzzleEnergyJ": 481,
      "primerType": "small_pistol",
      "cartridgeType": "rimless",
      "caseShape": "tapered",
      "caseMaterial": "brass",
      "projectileKind": "bullet",
      "bulletProfile": "round_nose",
      "closure": "bullet",
      "markingColor": null,
      "markingMeaning": null,
      "parentCartridgeId": null,
      "yearIntroduced": 1902,
      "designer": "Georg Luger",
      "specSource": "https://en.wikipedia.org/wiki/9%C3%9719mm_Parabellum",
      "specStandard": "cip",
      "dataConfidence": 0.9,
      "verifiedAt": "2026-09-08",
      "verifiedFields": [
        "case_length_mm",
        "max_pressure_mpa"
      ],
      "createdAt": "2025-01-15 12:00:00",
      "updatedAt": "2026-09-10 06:42:19",
      "version": "a3f1c2d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2",
      "provenance": {
        "sources": [
          "https://www.glock.com/en/products/pistols/g17-gen5"
        ],
        "sourceKinds": [
          {
            "url": "https://www.glock.com/en/products/pistols/g17-gen5",
            "kind": "manufacturer",
            "archivedFrom": null,
            "rank": 0,
            "tier": "authority",
            "basis": "declared",
            "evidence": null
          }
        ],
        "bestSourceKind": "manufacturer",
        "makerSource": "https://www.glock.com/en/products/pistols/g17-gen5",
        "evidence": "maker",
        "checks": {
          "raised": 3,
          "open": 0,
          "history": [
            {
              "task": "verify:firearms:glock-g17-gen5",
              "number": 1204,
              "check": "quality:firearm-weight-fits-its-size",
              "kind": "verify",
              "agent": "redback",
              "worker": "redback-046535",
              "status": "resolved",
              "decidedAt": "2026-09-25 07:42:39",
              "proposed": "Read https://us.glock.com/en/pistols/g17-gen5: agrees on barrel_length, weight.",
              "fields": [
                "barrel_length_mm",
                "weight_empty_g"
              ],
              "sources": [
                "https://us.glock.com/en/pistols/g17-gen5"
              ],
              "review": {
                "decision": "accepted",
                "at": null
              },
              "finding": {
                "why": "2.22 g/cm3 against a submachine-gun / carbon-steel median of 0.64; check the weight and the dimensions",
                "evidence": {
                  "apparentDensity": 2.218,
                  "categoryMedian": 0.635
                }
              }
            }
          ]
        },
        "dataConfidence": 0.95,
        "verifiedAt": "2026-09-08",
        "verifiedFields": [
          "barrel_length_mm",
          "weight_empty_g"
        ],
        "specSource": null,
        "updatedAt": "2026-09-08 04:10:22",
        "version": "ba5c1d9e06c279a5"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "per_page": 20,
    "total": 559,
    "totalPages": 28
  }
}
```

### Errors

| Status | Code | Message |
| --- | --- | --- |
| 400 | `VALIDATION_ERROR` | Validation error |
| 401 | `UNAUTHORIZED` | API key required |
| 403 | `FORBIDDEN` | Valid key, not permitted |
| 429 | `RATE_LIMITED` | Rate limit exceeded |
| 500 | `INTERNAL_ERROR` | Unexpected server error |

### Example

```bash
curl --request GET \
  --url 'https://api.gunspec.io/v1/calibers' \
  --header 'X-API-Key: your_key'
```

## client.calibers.listAutoPaging()

`GET /v1/calibers`

Auth: API key required  
Tier: Explorer+

Auto-paginate through all calibers matching the given filters. Returns an async iterator that fetches pages on demand, yielding individual Caliber objects.

### Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `page` | integer | no | Page number, from 1 to 10,000 |
| `per_page` | integer | no | Items per page (max 100) |
| `q` | string | no | Substring match on caliber name or alias |
| `cartridge_type` | string | no | Filter by cartridge type |
| `primer_type` | string | no | Filter by primer type |
| `action_type` | string | no | Only cartridges chambered by at least one firearm with this action, as stored and underscored. The vocabulary is /v1/firearms/action-types |
| `If-None-Match` | string | no | The `ETag` from a previous response. If the body would be byte-identical, the API answers `304 Not Modified` with no body. A 304 counts toward the per-minute rate limit but not the daily request allowance. |

### Response 200 (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `data` | Caliber[] | One page of results |
| `data[].id` | string | URL-safe slug identifying the cartridge, e.g. `9x19mm-parabellum`. |
| `data[].name` | string | Display name, as the maker writes it. |
| `data[].aliases` | string[] | Other names this cartridge is sold or written as. |
| `data[].natoDesignation` | string \| null | NATO designation where the cartridge has one, e.g. `9mm NATO`. Null otherwise. |
| `data[].bulletDiameterMm` | number \| null | Bullet diameter in millimetres. |
| `data[].neckDiameterMm` | number \| null | Case neck outside diameter in millimetres. |
| `data[].shoulderDiameterMm` | number \| null | Bottleneck cases only. |
| `data[].baseDiameterMm` | number \| null | Case base diameter in millimetres, ahead of the extractor groove. |
| `data[].rimDiameterMm` | number \| null | Rim diameter in millimetres. |
| `data[].rimThicknessMm` | number \| null | Rim thickness in millimetres. |
| `data[].caseLengthMm` | number \| null | Case length in millimetres. |
| `data[].overallLengthMm` | number \| null | Overall length in millimetres, stock extended where it folds or collapses. |
| `data[].bulletLengthMm` | number \| null | Typical projectile length. |
| `data[].maxPressureMpa` | number \| null | Maximum average pressure in megapascals, as the standards body specifies it. |
| `data[].maxPressurePsi` | number \| null | The same pressure in pounds per square inch, converted. |
| `data[].typicalBulletWeightG` | number \| null | Representative bullet weight in grams. Divide by 0.0648 for grains. |
| `data[].typicalMuzzleVelocityMps` | number \| null | Representative muzzle velocity in metres per second. |
| `data[].typicalMuzzleEnergyJ` | number \| null | Representative muzzle energy in joules. |
| `data[].primerType` | string \| null | Primer size and type, e.g. `small_pistol`, `large_rifle`, `rimfire`. |
| `data[].cartridgeType` | string \| null | Head type: `rimmed`, `rimless`, `semi_rimmed`, `rebated_rim`, `belted`, plus historical values like `cap_and_ball`. **Not the broad class.** The registry said `pistol`/`rifle`/`shotgun` until the column was read, and it holds neither. |
| `data[].caseShape` | string \| null | Case profile: `straight`, `bottleneck`, `tapered`, `belted`. |
| `data[].caseMaterial` | string \| null | What the case is made of: `brass`, `steel`, `aluminium`, `polymer`. |
| `data[].projectileKind` | string \| null | `none` is a blank. |
| `data[].bulletProfile` | string \| null | Bullet shape: `fmj`, `hollow_point`, `soft_point`, `spitzer`, `round_nose`. |
| `data[].closure` | string \| null | What seals the case mouth; a blank is a star crimp. |
| `data[].markingColor` | string \| null | Paint that identifies the load: crimp lacquer on a blank, tip colour on a military round. |
| `data[].markingMeaning` | string \| null | What that colour signifies, where the marking is a standard one. |
| `data[].parentCartridgeId` | string \| null | The cartridge this one was derived from, or null. A wildcat and its parent case, for example. |
| `data[].yearIntroduced` | integer \| null | Year it entered production or service. Null where the date is disputed or unknown. |
| `data[].designer` | string \| null | Person or team credited with the design, where it is known. |
| `data[].specSource` | string \| null | Page the dimensions were read from. |
| `data[].specStandard` | string \| null | The standards body whose figures these are (`SAAMI`, `CIP`), where the source names one. |
| `data[].dataConfidence` | 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. |
| `data[].verifiedAt` | string \| null | When a person last checked this record against its source. Null means nobody has: the row is model knowledge until then. |
| `data[].verifiedFields` | string[] | The fields the source stated. The same list is in `provenance.verifiedFields`. |
| `data[].createdAt` | string | When the record was first added, `YYYY-MM-DD HH:MM:SS` in UTC. |
| `data[].updatedAt` | 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. |
| `data[].version` | 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. |
| `data[].provenance` | Provenance | Where a record's figures came from and how far they have been checked, in one place. `sources` are the pages consulted; check a specific figure against those. `sourceKinds` says what each page is and `bestSourceKind` the strongest of them, on the hierarchy `SourceKind` defines: a maker or standards-body page is evidence for a figure, a retailer or forum page is evidence the item exists. `dataConfidence` is the 0 to 1 score set from what was actually sourced and never raised by hand. `verifiedAt` and `verifiedFields` say when a source was last read against the record and which fields it stated; null means the row is still seed knowledge. `updatedAt` and `version` are the same cache signals the record carries at the top level. |
| `data[].provenance.sources` | string[] | The pages consulted when the record was compiled, as an array. Check a specific figure against these rather than against `dataConfidence`. |
| `data[].provenance.sourceKinds` | object[] | What each cited page is, in `sources` order, on the `SourceKind` hierarchy. A host not in the source map is `other`, never guessed. |
| `data[].provenance.bestSourceKind` | SourceKind \| null | The strongest kind among the citations, or null when nothing is cited. `manufacturer` or `standards_body` means a figure can be checked against an authority; `retailer` or `community` alone means the record is still supported by copies of copies. |
| `data[].provenance.makerSource` | string \| null | The first cited page on the record's own manufacturer's website, or null when it cites none. A page on another maker's site does not count. Cited from the maker is not the same as checked against it: `verifiedFields` names the figures that were read against a source. |
| `data[].provenance.evidence` | string | How far the record's figures are backed, strongest first, for a badge: `verified` when at least one figure was read against a source (`verifiedFields`), `maker` when it cites a page on its own manufacturer's site (`makerSource`), `authority` when it cites a maker, standards body or government page, `secondary` when it cites only press, retailers, references or forums, `none` when it cites nothing. Derived from the fields beside it. |
| `data[].provenance.checks` | object | The checks the record has been through, on a single record's response only: every task the data agent (Redback) or a person was given about it, what each proposed, the pages it read, and whether a person accepted it. Nothing an agent proposes reaches the record until a person accepts it, so this is the trail behind `verifiedFields`. Who reviewed is not published; an agent's instance name is. |
| `data[].provenance.dataConfidence` | number \| null | The 0 to 1 score set from what was actually sourced, never raised by hand. See the confidence bands in the docs. |
| `data[].provenance.verifiedAt` | string \| null | When a source was last read against this record. Null means the row is still seed knowledge. |
| `data[].provenance.verifiedFields` | string[] | Which fields the source stated, as an array. Everything else on a verified row is still unverified. Null where no source has been read. |
| `data[].provenance.specSource` | string \| null | Cartridges only: the page the drawing figures were taken from. Null on every other kind of record. |
| `data[].provenance.updatedAt` | string | When a served column last changed; the same value the record carries at the top level. |
| `data[].provenance.version` | string \| null | The record's content version, equal to the top-level `version`; equal versions mean equal data. |
| `pagination.page` | number | Current page number |
| `pagination.per_page` | number | Items per page |
| `pagination.total` | number | Total matching records (Builder and above) |

```json
{
  "success": true,
  "data": [
    {
      "id": "9x19mm-parabellum",
      "name": "9x19mm Parabellum",
      "aliases": [
        "7.62x51mm NATO",
        ".308 Win"
      ],
      "natoDesignation": "9mm NATO",
      "bulletDiameterMm": 9.01,
      "neckDiameterMm": 9.65,
      "shoulderDiameterMm": null,
      "baseDiameterMm": 9.93,
      "rimDiameterMm": 9.96,
      "rimThicknessMm": 1.27,
      "caseLengthMm": 19.15,
      "overallLengthMm": 29.69,
      "bulletLengthMm": 15.5,
      "maxPressureMpa": 235,
      "maxPressurePsi": 34084,
      "typicalBulletWeightG": 7.45,
      "typicalMuzzleVelocityMps": 360,
      "typicalMuzzleEnergyJ": 481,
      "primerType": "small_pistol",
      "cartridgeType": "rimless",
      "caseShape": "tapered",
      "caseMaterial": "brass",
      "projectileKind": "bullet",
      "bulletProfile": "round_nose",
      "closure": "bullet",
      "markingColor": null,
      "markingMeaning": null,
      "parentCartridgeId": null,
      "yearIntroduced": 1902,
      "designer": "Georg Luger",
      "specSource": "https://en.wikipedia.org/wiki/9%C3%9719mm_Parabellum",
      "specStandard": "cip",
      "dataConfidence": 0.9,
      "verifiedAt": "2026-09-08",
      "verifiedFields": [
        "case_length_mm",
        "max_pressure_mpa"
      ],
      "createdAt": "2025-01-15 12:00:00",
      "updatedAt": "2026-09-10 06:42:19",
      "version": "a3f1c2d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2",
      "provenance": {
        "sources": [
          "https://www.glock.com/en/products/pistols/g17-gen5"
        ],
        "sourceKinds": [
          {
            "url": "https://www.glock.com/en/products/pistols/g17-gen5",
            "kind": "manufacturer",
            "archivedFrom": null,
            "rank": 0,
            "tier": "authority",
            "basis": "declared",
            "evidence": null
          }
        ],
        "bestSourceKind": "manufacturer",
        "makerSource": "https://www.glock.com/en/products/pistols/g17-gen5",
        "evidence": "maker",
        "checks": {
          "raised": 3,
          "open": 0,
          "history": [
            {
              "task": "verify:firearms:glock-g17-gen5",
              "number": 1204,
              "check": "quality:firearm-weight-fits-its-size",
              "kind": "verify",
              "agent": "redback",
              "worker": "redback-046535",
              "status": "resolved",
              "decidedAt": "2026-09-25 07:42:39",
              "proposed": "Read https://us.glock.com/en/pistols/g17-gen5: agrees on barrel_length, weight.",
              "fields": [
                "barrel_length_mm",
                "weight_empty_g"
              ],
              "sources": [
                "https://us.glock.com/en/pistols/g17-gen5"
              ],
              "review": {
                "decision": "accepted",
                "at": null
              },
              "finding": {
                "why": "2.22 g/cm3 against a submachine-gun / carbon-steel median of 0.64; check the weight and the dimensions",
                "evidence": {
                  "apparentDensity": 2.218,
                  "categoryMedian": 0.635
                }
              }
            }
          ]
        },
        "dataConfidence": 0.95,
        "verifiedAt": "2026-09-08",
        "verifiedFields": [
          "barrel_length_mm",
          "weight_empty_g"
        ],
        "specSource": null,
        "updatedAt": "2026-09-08 04:10:22",
        "version": "ba5c1d9e06c279a5"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "per_page": 20,
    "total": 559,
    "totalPages": 28
  }
}
```

### Errors

| Status | Code | Message |
| --- | --- | --- |
| 400 | `VALIDATION_ERROR` | Validation error |
| 401 | `UNAUTHORIZED` | API key required |
| 403 | `FORBIDDEN` | Valid key, not permitted |
| 429 | `RATE_LIMITED` | Rate limit exceeded |
| 500 | `INTERNAL_ERROR` | Unexpected server error |

### Notes

- Returns an async iterator: it fetches each page as you consume it, so a loop over the whole collection is one statement and one page in memory at a time.

### Example

```bash
curl --request GET \
  --url 'https://api.gunspec.io/v1/calibers' \
  --header 'X-API-Key: your_key'
```

## client.calibers.compare()

`GET /v1/calibers/compare`

Auth: API key required  
Tier: Builder+

Compare up to 5 calibers side by side.

### Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `ids` | string | yes | Comma-separated caliber slugs, maximum 5 |
| `If-None-Match` | string | no | The `ETag` from a previous response. If the body would be byte-identical, the API answers `304 Not Modified` with no body. A 304 counts toward the per-minute rate limit but not the daily request allowance. |

### Response 200 (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `data` | object | The response payload. |
| `data.items` | Caliber[] | The compared cartridges, in the order their ids were given. |
| `data.items[].id` | string | URL-safe slug identifying the cartridge, e.g. `9x19mm-parabellum`. |
| `data.items[].name` | string | Display name, as the maker writes it. |
| `data.items[].aliases` | string[] | Other names this cartridge is sold or written as. |
| `data.items[].natoDesignation` | string \| null | NATO designation where the cartridge has one, e.g. `9mm NATO`. Null otherwise. |
| `data.items[].bulletDiameterMm` | number \| null | Bullet diameter in millimetres. |
| `data.items[].neckDiameterMm` | number \| null | Case neck outside diameter in millimetres. |
| `data.items[].shoulderDiameterMm` | number \| null | Bottleneck cases only. |
| `data.items[].baseDiameterMm` | number \| null | Case base diameter in millimetres, ahead of the extractor groove. |
| `data.items[].rimDiameterMm` | number \| null | Rim diameter in millimetres. |
| `data.items[].rimThicknessMm` | number \| null | Rim thickness in millimetres. |
| `data.items[].caseLengthMm` | number \| null | Case length in millimetres. |
| `data.items[].overallLengthMm` | number \| null | Overall length in millimetres, stock extended where it folds or collapses. |
| `data.deltas` | object[] | Per-field differences across the compared cartridges, one entry per numeric field. |
| `data.deltas[].field` | string | Which column the row is about, named as the database names it. |
| `data.deltas[].values` | number \| null[] | This field on each compared cartridge, in the order the ids were given. Null where that record does not carry the field. |
| `data.deltas[].min` | number \| null | Lowest value across the compared cartridges. Null when fewer than two carry the field. |
| `data.deltas[].max` | number \| null | Highest value across the compared cartridges. Null when fewer than two carry the field. |
| `data.deltas[].percentDiff` | number \| null | Spread from the lowest value, as a percentage of it. Null when the minimum is zero or fewer than two carry the field. |

```json
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "9x19mm-parabellum",
        "name": "9x19mm Parabellum",
        "aliases": [
          "7.62x51mm NATO",
          ".308 Win"
        ],
        "natoDesignation": "9mm NATO",
        "bulletDiameterMm": 9.01,
        "neckDiameterMm": 9.65,
        "shoulderDiameterMm": null,
        "baseDiameterMm": 9.93,
        "rimDiameterMm": 9.96,
        "rimThicknessMm": 1.27,
        "caseLengthMm": 19.15,
        "overallLengthMm": 29.69,
        "bulletLengthMm": 15.5,
        "maxPressureMpa": 235,
        "maxPressurePsi": 34084,
        "typicalBulletWeightG": 7.45,
        "typicalMuzzleVelocityMps": 360,
        "typicalMuzzleEnergyJ": 481,
        "primerType": "small_pistol",
        "cartridgeType": "rimless",
        "caseShape": "tapered",
        "caseMaterial": "brass",
        "projectileKind": "bullet",
        "bulletProfile": "round_nose",
        "closure": "bullet",
        "markingColor": null,
        "markingMeaning": null,
        "parentCartridgeId": null,
        "yearIntroduced": 1902,
        "designer": "Georg Luger",
        "specSource": "https://en.wikipedia.org/wiki/9%C3%9719mm_Parabellum",
        "specStandard": "cip",
        "dataConfidence": 0.9,
        "verifiedAt": "2026-09-08",
        "verifiedFields": [
          "case_length_mm",
          "max_pressure_mpa"
        ],
        "createdAt": "2025-01-15 12:00:00",
        "updatedAt": "2026-09-10 06:42:19",
        "version": "a3f1c2d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2",
        "provenance": {
          "sources": [
            "https://www.glock.com/en/products/pistols/g17-gen5"
          ],
          "sourceKinds": [
            {
              "url": "https://www.glock.com/en/products/pistols/g17-gen5",
              "kind": "manufacturer",
              "archivedFrom": null,
              "rank": 0,
              "tier": "authority",
              "basis": "declared",
              "evidence": null
            }
          ],
          "bestSourceKind": "manufacturer",
          "makerSource": "https://www.glock.com/en/products/pistols/g17-gen5",
          "evidence": "maker",
          "checks": {
            "raised": 3,
            "open": 0,
            "history": [
              {
                "task": "verify:firearms:glock-g17-gen5",
                "number": 1204,
                "check": "quality:firearm-weight-fits-its-size",
                "kind": "verify",
                "agent": "redback",
                "worker": "redback-046535",
                "status": "resolved",
                "decidedAt": "2026-09-25 07:42:39",
                "proposed": "Read https://us.glock.com/en/pistols/g17-gen5: agrees on barrel_length, weight.",
                "fields": [
                  "barrel_length_mm",
                  "weight_empty_g"
                ],
                "sources": [
                  "https://us.glock.com/en/pistols/g17-gen5"
                ],
                "review": {
                  "decision": "accepted",
                  "at": null
                },
                "finding": {
                  "why": "2.22 g/cm3 against a submachine-gun / carbon-steel median of 0.64; check the weight and the dimensions",
                  "evidence": {
                    "apparentDensity": 2.218,
                    "categoryMedian": 0.635
                  }
                }
              }
            ]
          },
          "dataConfidence": 0.95,
          "verifiedAt": "2026-09-08",
          "verifiedFields": [
            "barrel_length_mm",
            "weight_empty_g"
          ],
          "specSource": null,
          "updatedAt": "2026-09-08 04:10:22",
          "version": "ba5c1d9e06c279a5"
        }
      }
    ],
    "deltas": [
      {
        "field": "caseLengthMm",
        "values": [
          19.15
        ],
        "min": 19.15,
        "max": 26.75,
        "percentDiff": 39.7
      }
    ]
  }
}
```

### Errors

| Status | Code | Message |
| --- | --- | --- |
| 400 | `VALIDATION_ERROR` | Validation error |
| 401 | `UNAUTHORIZED` | API key required |
| 403 | `FORBIDDEN` | Valid key, not permitted |
| 429 | `RATE_LIMITED` | Rate limit exceeded |
| 500 | `INTERNAL_ERROR` | Unexpected server error |

### Example

```bash
curl --request GET \
  --url 'https://api.gunspec.io/v1/calibers/compare?ids=9x19mm-parabellum,45-acp' \
  --header 'X-API-Key: your_key'
```

## client.calibers.ballistics()

`GET /v1/calibers/ballistics`

Auth: API key required  
Tier: Builder+

Get ballistics data for a caliber at a specified distance.

### Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Caliber slug |
| `distance` | integer | no | Maximum distance in metres |
| `If-None-Match` | string | no | The `ETag` from a previous response. If the body would be byte-identical, the API answers `304 Not Modified` with no body. A 304 counts toward the per-minute rate limit but not the daily request allowance. |

### Response 200 (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `data` | object | The response payload. |
| `data.caliber` | object | The cartridge the profile was computed for. |
| `data.caliber.id` | string | Slug of the cartridge. |
| `data.caliber.name` | string | Display name, as the maker writes it. |
| `data.distanceM` | integer | The distance the figures below are quoted at, echoing the `distance` parameter. |
| `data.muzzleVelocityMps` | number \| null | Muzzle velocity in metres per second, for the default load. |
| `data.muzzleEnergyJ` | number \| null | Muzzle energy in joules, for the default load. |
| `data.velocityAtDistanceMps` | number \| null | Remaining velocity at `distanceM`, in metres per second. |
| `data.energyAtDistanceJ` | number \| null | Remaining energy at `distanceM`, in joules. |
| `data.timeOfFlightS` | number \| null | Time of flight to `distanceM`, in seconds. |
| `data.bulletDropCm` | number \| null | Bullet drop below the line of sight at `distanceM`, in centimetres. |

```json
{
  "success": true,
  "data": {
    "caliber": {
      "id": "9x19mm-parabellum",
      "name": "9x19mm Parabellum"
    },
    "distanceM": 100,
    "muzzleVelocityMps": 360,
    "muzzleEnergyJ": 510,
    "velocityAtDistanceMps": 340,
    "energyAtDistanceJ": 455,
    "timeOfFlightS": 0.31,
    "bulletDropCm": -3.8
  }
}
```

### Errors

| Status | Code | Message |
| --- | --- | --- |
| 400 | `VALIDATION_ERROR` | Validation error |
| 401 | `UNAUTHORIZED` | API key required |
| 403 | `FORBIDDEN` | Valid key, not permitted |
| 404 | `NOT_FOUND` | Resource not found |
| 429 | `RATE_LIMITED` | Rate limit exceeded |
| 500 | `INTERNAL_ERROR` | Unexpected server error |

### Example

```bash
curl --request GET \
  --url 'https://api.gunspec.io/v1/calibers/ballistics?id=9x19mm-parabellum&distance=100' \
  --header 'X-API-Key: your_key'
```

## client.calibers.get()

`GET /v1/calibers/{id}`

Auth: API key required  
Tier: Explorer+

Get a single caliber by its slug or ID.

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Caliber slug |

### Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `If-None-Match` | string | no | The `ETag` from a previous response. If the body would be byte-identical, the API answers `304 Not Modified` with no body. A 304 counts toward the per-minute rate limit but not the daily request allowance. |

### Response 200 (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `data` | Caliber | The response payload |
| `data.id` | string | URL-safe slug identifying the cartridge, e.g. `9x19mm-parabellum`. |
| `data.name` | string | Display name, as the maker writes it. |
| `data.aliases` | string[] | Other names this cartridge is sold or written as. |
| `data.natoDesignation` | string \| null | NATO designation where the cartridge has one, e.g. `9mm NATO`. Null otherwise. |
| `data.bulletDiameterMm` | number \| null | Bullet diameter in millimetres. |
| `data.neckDiameterMm` | number \| null | Case neck outside diameter in millimetres. |
| `data.shoulderDiameterMm` | number \| null | Bottleneck cases only. |
| `data.baseDiameterMm` | number \| null | Case base diameter in millimetres, ahead of the extractor groove. |
| `data.rimDiameterMm` | number \| null | Rim diameter in millimetres. |
| `data.rimThicknessMm` | number \| null | Rim thickness in millimetres. |
| `data.caseLengthMm` | number \| null | Case length in millimetres. |
| `data.overallLengthMm` | number \| null | Overall length in millimetres, stock extended where it folds or collapses. |
| `data.bulletLengthMm` | number \| null | Typical projectile length. |
| `data.maxPressureMpa` | number \| null | Maximum average pressure in megapascals, as the standards body specifies it. |
| `data.maxPressurePsi` | number \| null | The same pressure in pounds per square inch, converted. |
| `data.typicalBulletWeightG` | number \| null | Representative bullet weight in grams. Divide by 0.0648 for grains. |
| `data.typicalMuzzleVelocityMps` | number \| null | Representative muzzle velocity in metres per second. |
| `data.typicalMuzzleEnergyJ` | number \| null | Representative muzzle energy in joules. |
| `data.primerType` | string \| null | Primer size and type, e.g. `small_pistol`, `large_rifle`, `rimfire`. |
| `data.cartridgeType` | string \| null | Head type: `rimmed`, `rimless`, `semi_rimmed`, `rebated_rim`, `belted`, plus historical values like `cap_and_ball`. **Not the broad class.** The registry said `pistol`/`rifle`/`shotgun` until the column was read, and it holds neither. |
| `data.caseShape` | string \| null | Case profile: `straight`, `bottleneck`, `tapered`, `belted`. |
| `data.caseMaterial` | string \| null | What the case is made of: `brass`, `steel`, `aluminium`, `polymer`. |
| `data.projectileKind` | string \| null | `none` is a blank. |
| `data.bulletProfile` | string \| null | Bullet shape: `fmj`, `hollow_point`, `soft_point`, `spitzer`, `round_nose`. |
| `data.closure` | string \| null | What seals the case mouth; a blank is a star crimp. |
| `data.markingColor` | string \| null | Paint that identifies the load: crimp lacquer on a blank, tip colour on a military round. |
| `data.markingMeaning` | string \| null | What that colour signifies, where the marking is a standard one. |
| `data.parentCartridgeId` | string \| null | The cartridge this one was derived from, or null. A wildcat and its parent case, for example. |
| `data.yearIntroduced` | integer \| null | Year it entered production or service. Null where the date is disputed or unknown. |
| `data.designer` | string \| null | Person or team credited with the design, where it is known. |
| `data.specSource` | string \| null | Page the dimensions were read from. |
| `data.specStandard` | string \| null | The standards body whose figures these are (`SAAMI`, `CIP`), where the source names one. |
| `data.dataConfidence` | 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. |
| `data.verifiedAt` | string \| null | When a person last checked this record against its source. Null means nobody has: the row is model knowledge until then. |
| `data.verifiedFields` | string[] | The fields the source stated. The same list is in `provenance.verifiedFields`. |
| `data.createdAt` | string | When the record was first added, `YYYY-MM-DD HH:MM:SS` in UTC. |
| `data.updatedAt` | 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. |
| `data.version` | 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. |
| `data.provenance` | Provenance | Where a record's figures came from and how far they have been checked, in one place. `sources` are the pages consulted; check a specific figure against those. `sourceKinds` says what each page is and `bestSourceKind` the strongest of them, on the hierarchy `SourceKind` defines: a maker or standards-body page is evidence for a figure, a retailer or forum page is evidence the item exists. `dataConfidence` is the 0 to 1 score set from what was actually sourced and never raised by hand. `verifiedAt` and `verifiedFields` say when a source was last read against the record and which fields it stated; null means the row is still seed knowledge. `updatedAt` and `version` are the same cache signals the record carries at the top level. |
| `data.provenance.sources` | string[] | The pages consulted when the record was compiled, as an array. Check a specific figure against these rather than against `dataConfidence`. |
| `data.provenance.sourceKinds` | object[] | What each cited page is, in `sources` order, on the `SourceKind` hierarchy. A host not in the source map is `other`, never guessed. |
| `data.provenance.bestSourceKind` | SourceKind \| null | The strongest kind among the citations, or null when nothing is cited. `manufacturer` or `standards_body` means a figure can be checked against an authority; `retailer` or `community` alone means the record is still supported by copies of copies. |
| `data.provenance.makerSource` | string \| null | The first cited page on the record's own manufacturer's website, or null when it cites none. A page on another maker's site does not count. Cited from the maker is not the same as checked against it: `verifiedFields` names the figures that were read against a source. |
| `data.provenance.evidence` | string | How far the record's figures are backed, strongest first, for a badge: `verified` when at least one figure was read against a source (`verifiedFields`), `maker` when it cites a page on its own manufacturer's site (`makerSource`), `authority` when it cites a maker, standards body or government page, `secondary` when it cites only press, retailers, references or forums, `none` when it cites nothing. Derived from the fields beside it. |
| `data.provenance.checks` | object | The checks the record has been through, on a single record's response only: every task the data agent (Redback) or a person was given about it, what each proposed, the pages it read, and whether a person accepted it. Nothing an agent proposes reaches the record until a person accepts it, so this is the trail behind `verifiedFields`. Who reviewed is not published; an agent's instance name is. |
| `data.provenance.dataConfidence` | number \| null | The 0 to 1 score set from what was actually sourced, never raised by hand. See the confidence bands in the docs. |
| `data.provenance.verifiedAt` | string \| null | When a source was last read against this record. Null means the row is still seed knowledge. |
| `data.provenance.verifiedFields` | string[] | Which fields the source stated, as an array. Everything else on a verified row is still unverified. Null where no source has been read. |
| `data.provenance.specSource` | string \| null | Cartridges only: the page the drawing figures were taken from. Null on every other kind of record. |
| `data.provenance.updatedAt` | string | When a served column last changed; the same value the record carries at the top level. |
| `data.provenance.version` | string \| null | The record's content version, equal to the top-level `version`; equal versions mean equal data. |

```json
{
  "success": true,
  "data": {
    "id": "9x19mm-parabellum",
    "name": "9x19mm Parabellum",
    "aliases": [
      "7.62x51mm NATO",
      ".308 Win"
    ],
    "natoDesignation": "9mm NATO",
    "bulletDiameterMm": 9.01,
    "neckDiameterMm": 9.65,
    "shoulderDiameterMm": null,
    "baseDiameterMm": 9.93,
    "rimDiameterMm": 9.96,
    "rimThicknessMm": 1.27,
    "caseLengthMm": 19.15,
    "overallLengthMm": 29.69,
    "bulletLengthMm": 15.5,
    "maxPressureMpa": 235,
    "maxPressurePsi": 34084,
    "typicalBulletWeightG": 7.45,
    "typicalMuzzleVelocityMps": 360,
    "typicalMuzzleEnergyJ": 481,
    "primerType": "small_pistol",
    "cartridgeType": "rimless",
    "caseShape": "tapered",
    "caseMaterial": "brass",
    "projectileKind": "bullet",
    "bulletProfile": "round_nose",
    "closure": "bullet",
    "markingColor": null,
    "markingMeaning": null,
    "parentCartridgeId": null,
    "yearIntroduced": 1902,
    "designer": "Georg Luger",
    "specSource": "https://en.wikipedia.org/wiki/9%C3%9719mm_Parabellum",
    "specStandard": "cip",
    "dataConfidence": 0.9,
    "verifiedAt": "2026-09-08",
    "verifiedFields": [
      "case_length_mm",
      "max_pressure_mpa"
    ],
    "createdAt": "2025-01-15 12:00:00",
    "updatedAt": "2026-09-10 06:42:19",
    "version": "a3f1c2d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2",
    "provenance": {
      "sources": [
        "https://www.glock.com/en/products/pistols/g17-gen5"
      ],
      "sourceKinds": [
        {
          "url": "https://www.glock.com/en/products/pistols/g17-gen5",
          "kind": "manufacturer",
          "archivedFrom": null,
          "rank": 0,
          "tier": "authority",
          "basis": "declared",
          "evidence": null
        }
      ],
      "bestSourceKind": "manufacturer",
      "makerSource": "https://www.glock.com/en/products/pistols/g17-gen5",
      "evidence": "maker",
      "checks": {
        "raised": 3,
        "open": 0,
        "history": [
          {
            "task": "verify:firearms:glock-g17-gen5",
            "number": 1204,
            "check": "quality:firearm-weight-fits-its-size",
            "kind": "verify",
            "agent": "redback",
            "worker": "redback-046535",
            "status": "resolved",
            "decidedAt": "2026-09-25 07:42:39",
            "proposed": "Read https://us.glock.com/en/pistols/g17-gen5: agrees on barrel_length, weight.",
            "fields": [
              "barrel_length_mm",
              "weight_empty_g"
            ],
            "sources": [
              "https://us.glock.com/en/pistols/g17-gen5"
            ],
            "review": {
              "decision": "accepted",
              "at": null
            },
            "finding": {
              "why": "2.22 g/cm3 against a submachine-gun / carbon-steel median of 0.64; check the weight and the dimensions",
              "evidence": {
                "apparentDensity": 2.218,
                "categoryMedian": 0.635
              }
            }
          }
        ]
      },
      "dataConfidence": 0.95,
      "verifiedAt": "2026-09-08",
      "verifiedFields": [
        "barrel_length_mm",
        "weight_empty_g"
      ],
      "specSource": null,
      "updatedAt": "2026-09-08 04:10:22",
      "version": "ba5c1d9e06c279a5"
    }
  }
}
```

### Errors

| Status | Code | Message |
| --- | --- | --- |
| 400 | `VALIDATION_ERROR` | Validation error |
| 401 | `UNAUTHORIZED` | API key required |
| 403 | `FORBIDDEN` | Valid key, not permitted |
| 404 | `NOT_FOUND` | Resource not found |
| 429 | `RATE_LIMITED` | Rate limit exceeded |
| 500 | `INTERNAL_ERROR` | Unexpected server error |

### Example

```bash
curl --request GET \
  --url 'https://api.gunspec.io/v1/calibers/9x19mm-parabellum' \
  --header 'X-API-Key: your_key'
```

## client.calibers.getFirearms()

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

Auth: API key required  
Tier: Explorer+

Get all firearms that use a specific caliber.

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Caliber slug |

### Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `page` | integer | no | Page number, from 1 to 10,000 |
| `per_page` | integer | no | Items per page (max 100) |

### Response 200 (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `data` | CaliberFirearmListItem[] | One page of results |
| `data[].id` | string | URL-safe slug identifying the firearm, e.g. `glock-g17-gen5`. |
| `data[].name` | string | Display name, as the maker writes it. |
| `data[].manufacturerId` | string | Slug of the manufacturer that makes it. Resolve against `/v1/manufacturers/{id}`. |
| `data[].categoryId` | string | Slug of the category it belongs to. Resolve against `/v1/categories`. |
| `data[].yearIntroduced` | integer \| null | Year it entered production or service. Null where the date is disputed or unknown. |
| `data[].status` | FirearmStatus \| null | Production status, one of `in_production`, `discontinued`, `out_of_production`, `in_service`, `limited_production`, `prototype`. Discontinued records are kept deliberately: a rifle built in 2004 wears parts nobody sells today. |
| `data[].actionType` | string \| null | How the action cycles: `short_recoil`, `gas_operated`, `blowback`, `bolt_action` and so on. An open vocabulary: the values in use are published on the schema as `x-gunspec-vocabulary`, and `GET /v1/firearms/action-types` is the live list. |
| `data[].countryOfOrigin` | string \| null | ISO 3166-1 alpha-2 code of where it was designed, e.g. `AT`. |
| `data[].svgLineArtUrl` | string \| null | Line-art silhouette, or null where none has been drawn. |
| `data[].model3dUrl` | string \| null | GLB model, or null where none is on file. |
| `data[].images` | InlineMediaItem[] | Every image this firearm has, silhouette first. Empty when none are on file. |
| `data[].images[].id` | integer | Autoincrementing image id. |
| `data[].images[].url` | string | Absolute, directly fetchable URL. |
| `data[].images[].kind` | MediaKind | What kind of asset a media row is. |
| `data[].images[].alt` | string \| null | Alt text, where one has been written. Null otherwise, so do not render an empty string. |
| `data[].images[].width` | integer \| null | Pixel width, where it is known. |
| `data[].images[].height` | integer \| null | Pixel height, where it is known. |
| `pagination.page` | number | Current page number |
| `pagination.per_page` | number | Items per page |
| `pagination.total` | number | Total matching records (Builder and above) |

```json
{
  "success": true,
  "data": [
    {
      "id": "glock-g17-gen5",
      "name": "Glock 17 Gen5",
      "manufacturerId": "glock",
      "categoryId": "pistol",
      "yearIntroduced": 2017,
      "status": "in_production",
      "actionType": "short_recoil",
      "countryOfOrigin": "AT",
      "svgLineArtUrl": null,
      "model3dUrl": null,
      "images": [
        {
          "id": 2572,
          "url": "https://api.gunspec.io/v1/firearms/ak-47/images/2572",
          "kind": "silhouette",
          "alt": "AK-47 right profile",
          "width": 1536,
          "height": 1024
        }
      ]
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "per_page": 20,
    "total": 559,
    "totalPages": 28
  }
}
```

### Errors

| Status | Code | Message |
| --- | --- | --- |
| 400 | `VALIDATION_ERROR` | Validation error |
| 401 | `UNAUTHORIZED` | API key required |
| 403 | `FORBIDDEN` | Valid key, not permitted |
| 404 | `NOT_FOUND` | Resource not found |
| 429 | `RATE_LIMITED` | Rate limit exceeded |
| 500 | `INTERNAL_ERROR` | Unexpected server error |

### Example

```bash
curl --request GET \
  --url 'https://api.gunspec.io/v1/calibers/9x19mm-parabellum/firearms' \
  --header 'X-API-Key: your_key'
```

## client.calibers.getParentChain()

`GET /v1/calibers/{id}/parent-chain`

Auth: API key required  
Tier: Builder+

Get the parent caliber chain (ancestry) for a caliber.

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Caliber slug |

### Response 200 (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `data` | Caliber[] | The response payload. |
| `data[].id` | string | URL-safe slug identifying the cartridge, e.g. `9x19mm-parabellum`. |
| `data[].name` | string | Display name, as the maker writes it. |
| `data[].aliases` | string[] | Other names this cartridge is sold or written as. |
| `data[].natoDesignation` | string \| null | NATO designation where the cartridge has one, e.g. `9mm NATO`. Null otherwise. |
| `data[].bulletDiameterMm` | number \| null | Bullet diameter in millimetres. |
| `data[].neckDiameterMm` | number \| null | Case neck outside diameter in millimetres. |
| `data[].shoulderDiameterMm` | number \| null | Bottleneck cases only. |
| `data[].baseDiameterMm` | number \| null | Case base diameter in millimetres, ahead of the extractor groove. |
| `data[].rimDiameterMm` | number \| null | Rim diameter in millimetres. |
| `data[].rimThicknessMm` | number \| null | Rim thickness in millimetres. |
| `data[].caseLengthMm` | number \| null | Case length in millimetres. |
| `data[].overallLengthMm` | number \| null | Overall length in millimetres, stock extended where it folds or collapses. |
| `data[].bulletLengthMm` | number \| null | Typical projectile length. |
| `data[].maxPressureMpa` | number \| null | Maximum average pressure in megapascals, as the standards body specifies it. |
| `data[].maxPressurePsi` | number \| null | The same pressure in pounds per square inch, converted. |
| `data[].typicalBulletWeightG` | number \| null | Representative bullet weight in grams. Divide by 0.0648 for grains. |
| `data[].typicalMuzzleVelocityMps` | number \| null | Representative muzzle velocity in metres per second. |
| `data[].typicalMuzzleEnergyJ` | number \| null | Representative muzzle energy in joules. |
| `data[].primerType` | string \| null | Primer size and type, e.g. `small_pistol`, `large_rifle`, `rimfire`. |
| `data[].cartridgeType` | string \| null | Head type: `rimmed`, `rimless`, `semi_rimmed`, `rebated_rim`, `belted`, plus historical values like `cap_and_ball`. **Not the broad class.** The registry said `pistol`/`rifle`/`shotgun` until the column was read, and it holds neither. |
| `data[].caseShape` | string \| null | Case profile: `straight`, `bottleneck`, `tapered`, `belted`. |
| `data[].caseMaterial` | string \| null | What the case is made of: `brass`, `steel`, `aluminium`, `polymer`. |
| `data[].projectileKind` | string \| null | `none` is a blank. |
| `data[].bulletProfile` | string \| null | Bullet shape: `fmj`, `hollow_point`, `soft_point`, `spitzer`, `round_nose`. |
| `data[].closure` | string \| null | What seals the case mouth; a blank is a star crimp. |
| `data[].markingColor` | string \| null | Paint that identifies the load: crimp lacquer on a blank, tip colour on a military round. |
| `data[].markingMeaning` | string \| null | What that colour signifies, where the marking is a standard one. |
| `data[].parentCartridgeId` | string \| null | The cartridge this one was derived from, or null. A wildcat and its parent case, for example. |
| `data[].yearIntroduced` | integer \| null | Year it entered production or service. Null where the date is disputed or unknown. |
| `data[].designer` | string \| null | Person or team credited with the design, where it is known. |
| `data[].specSource` | string \| null | Page the dimensions were read from. |
| `data[].specStandard` | string \| null | The standards body whose figures these are (`SAAMI`, `CIP`), where the source names one. |
| `data[].dataConfidence` | 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. |
| `data[].verifiedAt` | string \| null | When a person last checked this record against its source. Null means nobody has: the row is model knowledge until then. |
| `data[].verifiedFields` | string[] | The fields the source stated. The same list is in `provenance.verifiedFields`. |
| `data[].createdAt` | string | When the record was first added, `YYYY-MM-DD HH:MM:SS` in UTC. |
| `data[].updatedAt` | 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. |
| `data[].version` | 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. |
| `data[].provenance` | Provenance | Where a record's figures came from and how far they have been checked, in one place. `sources` are the pages consulted; check a specific figure against those. `sourceKinds` says what each page is and `bestSourceKind` the strongest of them, on the hierarchy `SourceKind` defines: a maker or standards-body page is evidence for a figure, a retailer or forum page is evidence the item exists. `dataConfidence` is the 0 to 1 score set from what was actually sourced and never raised by hand. `verifiedAt` and `verifiedFields` say when a source was last read against the record and which fields it stated; null means the row is still seed knowledge. `updatedAt` and `version` are the same cache signals the record carries at the top level. |
| `data[].provenance.sources` | string[] | The pages consulted when the record was compiled, as an array. Check a specific figure against these rather than against `dataConfidence`. |
| `data[].provenance.sourceKinds` | object[] | What each cited page is, in `sources` order, on the `SourceKind` hierarchy. A host not in the source map is `other`, never guessed. |
| `data[].provenance.bestSourceKind` | SourceKind \| null | The strongest kind among the citations, or null when nothing is cited. `manufacturer` or `standards_body` means a figure can be checked against an authority; `retailer` or `community` alone means the record is still supported by copies of copies. |
| `data[].provenance.makerSource` | string \| null | The first cited page on the record's own manufacturer's website, or null when it cites none. A page on another maker's site does not count. Cited from the maker is not the same as checked against it: `verifiedFields` names the figures that were read against a source. |
| `data[].provenance.evidence` | string | How far the record's figures are backed, strongest first, for a badge: `verified` when at least one figure was read against a source (`verifiedFields`), `maker` when it cites a page on its own manufacturer's site (`makerSource`), `authority` when it cites a maker, standards body or government page, `secondary` when it cites only press, retailers, references or forums, `none` when it cites nothing. Derived from the fields beside it. |
| `data[].provenance.checks` | object | The checks the record has been through, on a single record's response only: every task the data agent (Redback) or a person was given about it, what each proposed, the pages it read, and whether a person accepted it. Nothing an agent proposes reaches the record until a person accepts it, so this is the trail behind `verifiedFields`. Who reviewed is not published; an agent's instance name is. |
| `data[].provenance.dataConfidence` | number \| null | The 0 to 1 score set from what was actually sourced, never raised by hand. See the confidence bands in the docs. |
| `data[].provenance.verifiedAt` | string \| null | When a source was last read against this record. Null means the row is still seed knowledge. |
| `data[].provenance.verifiedFields` | string[] | Which fields the source stated, as an array. Everything else on a verified row is still unverified. Null where no source has been read. |
| `data[].provenance.specSource` | string \| null | Cartridges only: the page the drawing figures were taken from. Null on every other kind of record. |
| `data[].provenance.updatedAt` | string | When a served column last changed; the same value the record carries at the top level. |
| `data[].provenance.version` | string \| null | The record's content version, equal to the top-level `version`; equal versions mean equal data. |

```json
{
  "success": true,
  "data": [
    {
      "id": "9x19mm-parabellum",
      "name": "9x19mm Parabellum",
      "aliases": [
        "7.62x51mm NATO",
        ".308 Win"
      ],
      "natoDesignation": "9mm NATO",
      "bulletDiameterMm": 9.01,
      "neckDiameterMm": 9.65,
      "shoulderDiameterMm": null,
      "baseDiameterMm": 9.93,
      "rimDiameterMm": 9.96,
      "rimThicknessMm": 1.27,
      "caseLengthMm": 19.15,
      "overallLengthMm": 29.69,
      "bulletLengthMm": 15.5,
      "maxPressureMpa": 235,
      "maxPressurePsi": 34084,
      "typicalBulletWeightG": 7.45,
      "typicalMuzzleVelocityMps": 360,
      "typicalMuzzleEnergyJ": 481,
      "primerType": "small_pistol",
      "cartridgeType": "rimless",
      "caseShape": "tapered",
      "caseMaterial": "brass",
      "projectileKind": "bullet",
      "bulletProfile": "round_nose",
      "closure": "bullet",
      "markingColor": null,
      "markingMeaning": null,
      "parentCartridgeId": null,
      "yearIntroduced": 1902,
      "designer": "Georg Luger",
      "specSource": "https://en.wikipedia.org/wiki/9%C3%9719mm_Parabellum",
      "specStandard": "cip",
      "dataConfidence": 0.9,
      "verifiedAt": "2026-09-08",
      "verifiedFields": [
        "case_length_mm",
        "max_pressure_mpa"
      ],
      "createdAt": "2025-01-15 12:00:00",
      "updatedAt": "2026-09-10 06:42:19",
      "version": "a3f1c2d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2",
      "provenance": {
        "sources": [
          "https://www.glock.com/en/products/pistols/g17-gen5"
        ],
        "sourceKinds": [
          {
            "url": "https://www.glock.com/en/products/pistols/g17-gen5",
            "kind": "manufacturer",
            "archivedFrom": null,
            "rank": 0,
            "tier": "authority",
            "basis": "declared",
            "evidence": null
          }
        ],
        "bestSourceKind": "manufacturer",
        "makerSource": "https://www.glock.com/en/products/pistols/g17-gen5",
        "evidence": "maker",
        "checks": {
          "raised": 3,
          "open": 0,
          "history": [
            {
              "task": "verify:firearms:glock-g17-gen5",
              "number": 1204,
              "check": "quality:firearm-weight-fits-its-size",
              "kind": "verify",
              "agent": "redback",
              "worker": "redback-046535",
              "status": "resolved",
              "decidedAt": "2026-09-25 07:42:39",
              "proposed": "Read https://us.glock.com/en/pistols/g17-gen5: agrees on barrel_length, weight.",
              "fields": [
                "barrel_length_mm",
                "weight_empty_g"
              ],
              "sources": [
                "https://us.glock.com/en/pistols/g17-gen5"
              ],
              "review": {
                "decision": "accepted",
                "at": null
              },
              "finding": {
                "why": "2.22 g/cm3 against a submachine-gun / carbon-steel median of 0.64; check the weight and the dimensions",
                "evidence": {
                  "apparentDensity": 2.218,
                  "categoryMedian": 0.635
                }
              }
            }
          ]
        },
        "dataConfidence": 0.95,
        "verifiedAt": "2026-09-08",
        "verifiedFields": [
          "barrel_length_mm",
          "weight_empty_g"
        ],
        "specSource": null,
        "updatedAt": "2026-09-08 04:10:22",
        "version": "ba5c1d9e06c279a5"
      }
    }
  ]
}
```

### Errors

| Status | Code | Message |
| --- | --- | --- |
| 400 | `VALIDATION_ERROR` | Validation error |
| 401 | `UNAUTHORIZED` | API key required |
| 403 | `FORBIDDEN` | Valid key, not permitted |
| 404 | `NOT_FOUND` | Resource not found |
| 429 | `RATE_LIMITED` | Rate limit exceeded |
| 500 | `INTERNAL_ERROR` | Unexpected server error |

### Example

```bash
curl --request GET \
  --url 'https://api.gunspec.io/v1/calibers/9x19mm-parabellum/parent-chain' \
  --header 'X-API-Key: your_key'
```

## client.calibers.getFamily()

`GET /v1/calibers/{id}/family`

Auth: API key required  
Tier: Builder+

Get the full family tree of related calibers.

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Caliber slug |

### Response 200 (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `data` | object | The response payload. |
| `data.ancestors` | Caliber[] | The parent chain, nearest first, up to 20 deep. |
| `data.ancestors[].id` | string | URL-safe slug identifying the cartridge, e.g. `9x19mm-parabellum`. |
| `data.ancestors[].name` | string | Display name, as the maker writes it. |
| `data.ancestors[].aliases` | string[] | Other names this cartridge is sold or written as. |
| `data.ancestors[].natoDesignation` | string \| null | NATO designation where the cartridge has one, e.g. `9mm NATO`. Null otherwise. |
| `data.ancestors[].bulletDiameterMm` | number \| null | Bullet diameter in millimetres. |
| `data.ancestors[].neckDiameterMm` | number \| null | Case neck outside diameter in millimetres. |
| `data.ancestors[].shoulderDiameterMm` | number \| null | Bottleneck cases only. |
| `data.ancestors[].baseDiameterMm` | number \| null | Case base diameter in millimetres, ahead of the extractor groove. |
| `data.ancestors[].rimDiameterMm` | number \| null | Rim diameter in millimetres. |
| `data.ancestors[].rimThicknessMm` | number \| null | Rim thickness in millimetres. |
| `data.ancestors[].caseLengthMm` | number \| null | Case length in millimetres. |
| `data.ancestors[].overallLengthMm` | number \| null | Overall length in millimetres, stock extended where it folds or collapses. |
| `data.current` | Caliber |  |
| `data.current.id` | string | URL-safe slug identifying the cartridge, e.g. `9x19mm-parabellum`. |
| `data.current.name` | string | Display name, as the maker writes it. |
| `data.current.aliases` | string[] | Other names this cartridge is sold or written as. |
| `data.current.natoDesignation` | string \| null | NATO designation where the cartridge has one, e.g. `9mm NATO`. Null otherwise. |
| `data.current.bulletDiameterMm` | number \| null | Bullet diameter in millimetres. |
| `data.current.neckDiameterMm` | number \| null | Case neck outside diameter in millimetres. |
| `data.current.shoulderDiameterMm` | number \| null | Bottleneck cases only. |
| `data.current.baseDiameterMm` | number \| null | Case base diameter in millimetres, ahead of the extractor groove. |
| `data.current.rimDiameterMm` | number \| null | Rim diameter in millimetres. |
| `data.current.rimThicknessMm` | number \| null | Rim thickness in millimetres. |
| `data.current.caseLengthMm` | number \| null | Case length in millimetres. |
| `data.current.overallLengthMm` | number \| null | Overall length in millimetres, stock extended where it folds or collapses. |
| `data.descendants` | Caliber[] | Every cartridge derived from this one, up to 50 rows. |
| `data.descendants[].id` | string | URL-safe slug identifying the cartridge, e.g. `9x19mm-parabellum`. |
| `data.descendants[].name` | string | Display name, as the maker writes it. |
| `data.descendants[].aliases` | string[] | Other names this cartridge is sold or written as. |
| `data.descendants[].natoDesignation` | string \| null | NATO designation where the cartridge has one, e.g. `9mm NATO`. Null otherwise. |
| `data.descendants[].bulletDiameterMm` | number \| null | Bullet diameter in millimetres. |
| `data.descendants[].neckDiameterMm` | number \| null | Case neck outside diameter in millimetres. |
| `data.descendants[].shoulderDiameterMm` | number \| null | Bottleneck cases only. |
| `data.descendants[].baseDiameterMm` | number \| null | Case base diameter in millimetres, ahead of the extractor groove. |
| `data.descendants[].rimDiameterMm` | number \| null | Rim diameter in millimetres. |
| `data.descendants[].rimThicknessMm` | number \| null | Rim thickness in millimetres. |
| `data.descendants[].caseLengthMm` | number \| null | Case length in millimetres. |
| `data.descendants[].overallLengthMm` | number \| null | Overall length in millimetres, stock extended where it folds or collapses. |

```json
{
  "success": true,
  "data": {
    "ancestors": [
      {
        "id": "9x19mm-parabellum",
        "name": "9x19mm Parabellum",
        "aliases": [
          "7.62x51mm NATO",
          ".308 Win"
        ],
        "natoDesignation": "9mm NATO",
        "bulletDiameterMm": 9.01,
        "neckDiameterMm": 9.65,
        "shoulderDiameterMm": null,
        "baseDiameterMm": 9.93,
        "rimDiameterMm": 9.96,
        "rimThicknessMm": 1.27,
        "caseLengthMm": 19.15,
        "overallLengthMm": 29.69,
        "bulletLengthMm": 15.5,
        "maxPressureMpa": 235,
        "maxPressurePsi": 34084,
        "typicalBulletWeightG": 7.45,
        "typicalMuzzleVelocityMps": 360,
        "typicalMuzzleEnergyJ": 481,
        "primerType": "small_pistol",
        "cartridgeType": "rimless",
        "caseShape": "tapered",
        "caseMaterial": "brass",
        "projectileKind": "bullet",
        "bulletProfile": "round_nose",
        "closure": "bullet",
        "markingColor": null,
        "markingMeaning": null,
        "parentCartridgeId": null,
        "yearIntroduced": 1902,
        "designer": "Georg Luger",
        "specSource": "https://en.wikipedia.org/wiki/9%C3%9719mm_Parabellum",
        "specStandard": "cip",
        "dataConfidence": 0.9,
        "verifiedAt": "2026-09-08",
        "verifiedFields": [
          "case_length_mm",
          "max_pressure_mpa"
        ],
        "createdAt": "2025-01-15 12:00:00",
        "updatedAt": "2026-09-10 06:42:19",
        "version": "a3f1c2d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2",
        "provenance": {
          "sources": [
            "https://www.glock.com/en/products/pistols/g17-gen5"
          ],
          "sourceKinds": [
            {
              "url": "https://www.glock.com/en/products/pistols/g17-gen5",
              "kind": "manufacturer",
              "archivedFrom": null,
              "rank": 0,
              "tier": "authority",
              "basis": "declared",
              "evidence": null
            }
          ],
          "bestSourceKind": "manufacturer",
          "makerSource": "https://www.glock.com/en/products/pistols/g17-gen5",
          "evidence": "maker",
          "checks": {
            "raised": 3,
            "open": 0,
            "history": [
              {
                "task": "verify:firearms:glock-g17-gen5",
                "number": 1204,
                "check": "quality:firearm-weight-fits-its-size",
                "kind": "verify",
                "agent": "redback",
                "worker": "redback-046535",
                "status": "resolved",
                "decidedAt": "2026-09-25 07:42:39",
                "proposed": "Read https://us.glock.com/en/pistols/g17-gen5: agrees on barrel_length, weight.",
                "fields": [
                  "barrel_length_mm",
                  "weight_empty_g"
                ],
                "sources": [
                  "https://us.glock.com/en/pistols/g17-gen5"
                ],
                "review": {
                  "decision": "accepted",
                  "at": null
                },
                "finding": {
                  "why": "2.22 g/cm3 against a submachine-gun / carbon-steel median of 0.64; check the weight and the dimensions",
                  "evidence": {
                    "apparentDensity": 2.218,
                    "categoryMedian": 0.635
                  }
                }
              }
            ]
          },
          "dataConfidence": 0.95,
          "verifiedAt": "2026-09-08",
          "verifiedFields": [
            "barrel_length_mm",
            "weight_empty_g"
          ],
          "specSource": null,
          "updatedAt": "2026-09-08 04:10:22",
          "version": "ba5c1d9e06c279a5"
        }
      }
    ],
    "current": {
      "id": "9x19mm-parabellum",
      "name": "9x19mm Parabellum",
      "aliases": [
        "7.62x51mm NATO",
        ".308 Win"
      ],
      "natoDesignation": "9mm NATO",
      "bulletDiameterMm": 9.01,
      "neckDiameterMm": 9.65,
      "shoulderDiameterMm": null,
      "baseDiameterMm": 9.93,
      "rimDiameterMm": 9.96,
      "rimThicknessMm": 1.27,
      "caseLengthMm": 19.15,
      "overallLengthMm": 29.69,
      "bulletLengthMm": 15.5,
      "maxPressureMpa": 235,
      "maxPressurePsi": 34084,
      "typicalBulletWeightG": 7.45,
      "typicalMuzzleVelocityMps": 360,
      "typicalMuzzleEnergyJ": 481,
      "primerType": "small_pistol",
      "cartridgeType": "rimless",
      "caseShape": "tapered",
      "caseMaterial": "brass",
      "projectileKind": "bullet",
      "bulletProfile": "round_nose",
      "closure": "bullet",
      "markingColor": null,
      "markingMeaning": null,
      "parentCartridgeId": null,
      "yearIntroduced": 1902,
      "designer": "Georg Luger",
      "specSource": "https://en.wikipedia.org/wiki/9%C3%9719mm_Parabellum",
      "specStandard": "cip",
      "dataConfidence": 0.9,
      "verifiedAt": "2026-09-08",
      "verifiedFields": [
        "case_length_mm",
        "max_pressure_mpa"
      ],
      "createdAt": "2025-01-15 12:00:00",
      "updatedAt": "2026-09-10 06:42:19",
      "version": "a3f1c2d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2",
      "provenance": {
        "sources": [
          "https://www.glock.com/en/products/pistols/g17-gen5"
        ],
        "sourceKinds": [
          {
            "url": "https://www.glock.com/en/products/pistols/g17-gen5",
            "kind": "manufacturer",
            "archivedFrom": null,
            "rank": 0,
            "tier": "authority",
            "basis": "declared",
            "evidence": null
          }
        ],
        "bestSourceKind": "manufacturer",
        "makerSource": "https://www.glock.com/en/products/pistols/g17-gen5",
        "evidence": "maker",
        "checks": {
          "raised": 3,
          "open": 0,
          "history": [
            {
              "task": "verify:firearms:glock-g17-gen5",
              "number": 1204,
              "check": "quality:firearm-weight-fits-its-size",
              "kind": "verify",
              "agent": "redback",
              "worker": "redback-046535",
              "status": "resolved",
              "decidedAt": "2026-09-25 07:42:39",
              "proposed": "Read https://us.glock.com/en/pistols/g17-gen5: agrees on barrel_length, weight.",
              "fields": [
                "barrel_length_mm",
                "weight_empty_g"
              ],
              "sources": [
                "https://us.glock.com/en/pistols/g17-gen5"
              ],
              "review": {
                "decision": "accepted",
                "at": null
              },
              "finding": {
                "why": "2.22 g/cm3 against a submachine-gun / carbon-steel median of 0.64; check the weight and the dimensions",
                "evidence": {
                  "apparentDensity": 2.218,
                  "categoryMedian": 0.635
                }
              }
            }
          ]
        },
        "dataConfidence": 0.95,
        "verifiedAt": "2026-09-08",
        "verifiedFields": [
          "barrel_length_mm",
          "weight_empty_g"
        ],
        "specSource": null,
        "updatedAt": "2026-09-08 04:10:22",
        "version": "ba5c1d9e06c279a5"
      }
    },
    "descendants": [
      {
        "id": "9x19mm-parabellum",
        "name": "9x19mm Parabellum",
        "aliases": [
          "7.62x51mm NATO",
          ".308 Win"
        ],
        "natoDesignation": "9mm NATO",
        "bulletDiameterMm": 9.01,
        "neckDiameterMm": 9.65,
        "shoulderDiameterMm": null,
        "baseDiameterMm": 9.93,
        "rimDiameterMm": 9.96,
        "rimThicknessMm": 1.27,
        "caseLengthMm": 19.15,
        "overallLengthMm": 29.69,
        "bulletLengthMm": 15.5,
        "maxPressureMpa": 235,
        "maxPressurePsi": 34084,
        "typicalBulletWeightG": 7.45,
        "typicalMuzzleVelocityMps": 360,
        "typicalMuzzleEnergyJ": 481,
        "primerType": "small_pistol",
        "cartridgeType": "rimless",
        "caseShape": "tapered",
        "caseMaterial": "brass",
        "projectileKind": "bullet",
        "bulletProfile": "round_nose",
        "closure": "bullet",
        "markingColor": null,
        "markingMeaning": null,
        "parentCartridgeId": null,
        "yearIntroduced": 1902,
        "designer": "Georg Luger",
        "specSource": "https://en.wikipedia.org/wiki/9%C3%9719mm_Parabellum",
        "specStandard": "cip",
        "dataConfidence": 0.9,
        "verifiedAt": "2026-09-08",
        "verifiedFields": [
          "case_length_mm",
          "max_pressure_mpa"
        ],
        "createdAt": "2025-01-15 12:00:00",
        "updatedAt": "2026-09-10 06:42:19",
        "version": "a3f1c2d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2",
        "provenance": {
          "sources": [
            "https://www.glock.com/en/products/pistols/g17-gen5"
          ],
          "sourceKinds": [
            {
              "url": "https://www.glock.com/en/products/pistols/g17-gen5",
              "kind": "manufacturer",
              "archivedFrom": null,
              "rank": 0,
              "tier": "authority",
              "basis": "declared",
              "evidence": null
            }
          ],
          "bestSourceKind": "manufacturer",
          "makerSource": "https://www.glock.com/en/products/pistols/g17-gen5",
          "evidence": "maker",
          "checks": {
            "raised": 3,
            "open": 0,
            "history": [
              {
                "task": "verify:firearms:glock-g17-gen5",
                "number": 1204,
                "check": "quality:firearm-weight-fits-its-size",
                "kind": "verify",
                "agent": "redback",
                "worker": "redback-046535",
                "status": "resolved",
                "decidedAt": "2026-09-25 07:42:39",
                "proposed": "Read https://us.glock.com/en/pistols/g17-gen5: agrees on barrel_length, weight.",
                "fields": [
                  "barrel_length_mm",
                  "weight_empty_g"
                ],
                "sources": [
                  "https://us.glock.com/en/pistols/g17-gen5"
                ],
                "review": {
                  "decision": "accepted",
                  "at": null
                },
                "finding": {
                  "why": "2.22 g/cm3 against a submachine-gun / carbon-steel median of 0.64; check the weight and the dimensions",
                  "evidence": {
                    "apparentDensity": 2.218,
                    "categoryMedian": 0.635
                  }
                }
              }
            ]
          },
          "dataConfidence": 0.95,
          "verifiedAt": "2026-09-08",
          "verifiedFields": [
            "barrel_length_mm",
            "weight_empty_g"
          ],
          "specSource": null,
          "updatedAt": "2026-09-08 04:10:22",
          "version": "ba5c1d9e06c279a5"
        }
      }
    ]
  }
}
```

### Errors

| Status | Code | Message |
| --- | --- | --- |
| 400 | `VALIDATION_ERROR` | Validation error |
| 401 | `UNAUTHORIZED` | API key required |
| 403 | `FORBIDDEN` | Valid key, not permitted |
| 404 | `NOT_FOUND` | Resource not found |
| 429 | `RATE_LIMITED` | Rate limit exceeded |
| 500 | `INTERNAL_ERROR` | Unexpected server error |

### Example

```bash
curl --request GET \
  --url 'https://api.gunspec.io/v1/calibers/9x19mm-parabellum/family' \
  --header 'X-API-Key: your_key'
```

## client.calibers.getAmmunition()

`GET /v1/calibers/{id}/ammunition`

Auth: API key required  
Tier: Builder+

Get ammunition loads available for a caliber.

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Caliber slug |

### Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `page` | integer | no | Page number, from 1 to 10,000 |
| `per_page` | integer | no | Items per page (max 100) |

### Response 200 (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `data` | AmmunitionLoad[] | A specific factory ammunition load within a caliber. |
| `data[].id` | string | URL-safe slug identifying the load, e.g. `9mm-124gr-fmj`. |
| `data[].caliberId` | string | Slug of the cartridge. Resolve against `/v1/calibers/{id}`. |
| `data[].name` | string | Display name, as the maker writes it. |
| `data[].designation` | string \| null | Military or standards designation for the load, e.g. `M882`. |
| `data[].manufacturer` | string \| null | Who makes it. |
| `data[].countryOfOrigin` | string \| null | ISO 3166-1 alpha-2 code of where it was designed, e.g. `AT`. |
| `data[].bulletWeightG` | number | Bullet weight in grams. Divide by 0.0648 for grains. |
| `data[].bulletType` | string | Bullet construction: `fmj`, `jhp`, `sp`, `match`. |
| `data[].ballisticCoefficientG1` | number \| null | Ballistic coefficient against the G1 drag model, the convention for flat-based bullets. |
| `data[].ballisticCoefficientG7` | number \| null | Ballistic coefficient against the G7 drag model, which fits boat-tail rifle bullets better than G1. |
| `data[].referenceVelocityMps` | number | Muzzle velocity in metres per second **at the reference barrel length below**, not at the barrel of any particular firearm. |
| `data[].referenceBarrelLengthMm` | number | Barrel length in millimetres the reference velocity was measured from. Velocity for another barrel is extrapolated from this pair. |
| `data[].sectionalDensity` | number \| null | Bullet mass divided by the square of its diameter. Higher penetrates better, all else equal. |
| `data[].yearIntroduced` | integer \| null | Year it entered production or service. Null where the date is disputed or unknown. |
| `data[].isCommon` | integer | 1 when the load is commonly stocked. |
| `data[].bulletSvgUrl` | string \| null | Absolute URL of the rendered bullet diagram. |
| `data[].velocityRetentionExponent` | number \| null | Decay exponent used when extrapolating velocity to a barrel length other than the reference one. |
| `data[].description` | string \| null | Prose summary of the record, where one has been written. |
| `data[].isMilitary` | integer | 1 when the load is a military issue round. |
| `data[].createdAt` | string | When the record was first added, `YYYY-MM-DD HH:MM:SS` in UTC. |
| `data[].updatedAt` | 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 figure and does not move for a re-run of the seed. Before migration 0113 the seed rewrote every load on every run, and this said so. |
| `data[].sources` | string[] | Where the figures came from. Null on every load today: this family has somewhere to cite from and nothing cited yet, which the data quality page counts as a gap rather than hiding. Check a velocity against the maker until it is filled. |
| `data[].dataConfidence` | number \| null | How well specified the load is, on the same 0 to 1 scale as a firearm's. Null until there are sources behind it: a confidence figure with nothing supporting it looks like evidence and is not. |
| `data[].provenance` | Provenance | Where a record's figures came from and how far they have been checked, in one place. `sources` are the pages consulted; check a specific figure against those. `sourceKinds` says what each page is and `bestSourceKind` the strongest of them, on the hierarchy `SourceKind` defines: a maker or standards-body page is evidence for a figure, a retailer or forum page is evidence the item exists. `dataConfidence` is the 0 to 1 score set from what was actually sourced and never raised by hand. `verifiedAt` and `verifiedFields` say when a source was last read against the record and which fields it stated; null means the row is still seed knowledge. `updatedAt` and `version` are the same cache signals the record carries at the top level. |
| `data[].provenance.sources` | string[] | The pages consulted when the record was compiled, as an array. Check a specific figure against these rather than against `dataConfidence`. |
| `data[].provenance.sourceKinds` | object[] | What each cited page is, in `sources` order, on the `SourceKind` hierarchy. A host not in the source map is `other`, never guessed. |
| `data[].provenance.bestSourceKind` | SourceKind \| null | The strongest kind among the citations, or null when nothing is cited. `manufacturer` or `standards_body` means a figure can be checked against an authority; `retailer` or `community` alone means the record is still supported by copies of copies. |
| `data[].provenance.makerSource` | string \| null | The first cited page on the record's own manufacturer's website, or null when it cites none. A page on another maker's site does not count. Cited from the maker is not the same as checked against it: `verifiedFields` names the figures that were read against a source. |
| `data[].provenance.evidence` | string | How far the record's figures are backed, strongest first, for a badge: `verified` when at least one figure was read against a source (`verifiedFields`), `maker` when it cites a page on its own manufacturer's site (`makerSource`), `authority` when it cites a maker, standards body or government page, `secondary` when it cites only press, retailers, references or forums, `none` when it cites nothing. Derived from the fields beside it. |
| `data[].provenance.checks` | object | The checks the record has been through, on a single record's response only: every task the data agent (Redback) or a person was given about it, what each proposed, the pages it read, and whether a person accepted it. Nothing an agent proposes reaches the record until a person accepts it, so this is the trail behind `verifiedFields`. Who reviewed is not published; an agent's instance name is. |
| `data[].provenance.dataConfidence` | number \| null | The 0 to 1 score set from what was actually sourced, never raised by hand. See the confidence bands in the docs. |
| `data[].provenance.verifiedAt` | string \| null | When a source was last read against this record. Null means the row is still seed knowledge. |
| `data[].provenance.verifiedFields` | string[] | Which fields the source stated, as an array. Everything else on a verified row is still unverified. Null where no source has been read. |
| `data[].provenance.specSource` | string \| null | Cartridges only: the page the drawing figures were taken from. Null on every other kind of record. |
| `data[].provenance.updatedAt` | string | When a served column last changed; the same value the record carries at the top level. |
| `data[].provenance.version` | string \| null | The record's content version, equal to the top-level `version`; equal versions mean equal data. |
| `data[].version` | 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. |
| `pagination.page` | number | Current page number |
| `pagination.per_page` | number | Items per page |
| `pagination.total` | number | Total matching records (Builder and above) |

```json
{
  "success": true,
  "data": [
    {
      "id": "m882",
      "caliberId": "9x19mm-parabellum",
      "name": "9mm 124gr FMJ",
      "designation": "M882",
      "manufacturer": "Federal",
      "countryOfOrigin": "US",
      "bulletWeightG": 8.03,
      "bulletType": "fmj",
      "ballisticCoefficientG1": 0.165,
      "ballisticCoefficientG7": null,
      "referenceVelocityMps": 360,
      "referenceBarrelLengthMm": 114,
      "sectionalDensity": 0.14,
      "yearIntroduced": 1985,
      "isCommon": 1,
      "bulletSvgUrl": null,
      "velocityRetentionExponent": 0.12,
      "description": "Special purpose subsonic 7.62x39mm ammunition.",
      "isMilitary": 1,
      "createdAt": "2026-05-26 12:30:51",
      "updatedAt": "2026-05-26 12:30:51",
      "sources": [
        "string"
      ],
      "dataConfidence": null,
      "provenance": {
        "sources": [
          "https://www.glock.com/en/products/pistols/g17-gen5"
        ],
        "sourceKinds": [
          {
            "url": "https://www.glock.com/en/products/pistols/g17-gen5",
            "kind": "manufacturer",
            "archivedFrom": null,
            "rank": 0,
            "tier": "authority",
            "basis": "declared",
            "evidence": null
          }
        ],
        "bestSourceKind": "manufacturer",
        "makerSource": "https://www.glock.com/en/products/pistols/g17-gen5",
        "evidence": "maker",
        "checks": {
          "raised": 3,
          "open": 0,
          "history": [
            {
              "task": "verify:firearms:glock-g17-gen5",
              "number": 1204,
              "check": "quality:firearm-weight-fits-its-size",
              "kind": "verify",
              "agent": "redback",
              "worker": "redback-046535",
              "status": "resolved",
              "decidedAt": "2026-09-25 07:42:39",
              "proposed": "Read https://us.glock.com/en/pistols/g17-gen5: agrees on barrel_length, weight.",
              "fields": [
                "barrel_length_mm",
                "weight_empty_g"
              ],
              "sources": [
                "https://us.glock.com/en/pistols/g17-gen5"
              ],
              "review": {
                "decision": "accepted",
                "at": null
              },
              "finding": {
                "why": "2.22 g/cm3 against a submachine-gun / carbon-steel median of 0.64; check the weight and the dimensions",
                "evidence": {
                  "apparentDensity": 2.218,
                  "categoryMedian": 0.635
                }
              }
            }
          ]
        },
        "dataConfidence": 0.95,
        "verifiedAt": "2026-09-08",
        "verifiedFields": [
          "barrel_length_mm",
          "weight_empty_g"
        ],
        "specSource": null,
        "updatedAt": "2026-09-08 04:10:22",
        "version": "ba5c1d9e06c279a5"
      },
      "version": "e27e61b4b373f5a621d6ae5c67d521096131f2cf56300a1b2c3d4e5f6a7b8c9d"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "per_page": 20,
    "total": 559,
    "totalPages": 28
  }
}
```

### Errors

| Status | Code | Message |
| --- | --- | --- |
| 400 | `VALIDATION_ERROR` | Validation error |
| 401 | `UNAUTHORIZED` | API key required |
| 403 | `FORBIDDEN` | Valid key, not permitted |
| 404 | `NOT_FOUND` | Resource not found |
| 429 | `RATE_LIMITED` | Rate limit exceeded |
| 500 | `INTERNAL_ERROR` | Unexpected server error |

### Example

```bash
curl --request GET \
  --url 'https://api.gunspec.io/v1/calibers/9x19mm-parabellum/ammunition' \
  --header 'X-API-Key: your_key'
```
