# Manufacturers

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

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

## client.manufacturers.list()

`GET /v1/manufacturers`

Auth: API key required  
Tier: Explorer+

List manufacturers 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) |
| `country` | string | no | Filter by ISO 3166-1 alpha-2 country code |
| `search` | string | no | Substring match on manufacturer name |
| `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` | Manufacturer[] | One page of results |
| `data[].id` | string | URL-safe slug identifying the manufacturer, e.g. `glock`. |
| `data[].name` | string | Display name, as the maker writes it. |
| `data[].countryCode` | string \| null | ISO 3166-1 alpha-2 code of where the company is based. |
| `data[].foundedYear` | integer \| null | Year the company was founded. |
| `data[].status` | string \| null | Whether the company still trades. Much of this catalog is historical, and null means nobody has established which. |
| `data[].defunctYear` | integer \| null | Year the company ceased trading, where that is established. Null while it still trades, and also null where the closing year is simply unknown, so an absent value is never read as "still trading". |
| `data[].stateOwned` | boolean \| null | Government owned. **Null is not false**: it means nobody has established it, and a consumer treating null as false would report every unchecked company as private. |
| `data[].makesFirearms` | boolean \| null | Builds the products this catalog lists, as against importing, rebranding or customising them. Scope is the catalog rather than a legal definition: air rifles are listed here as rifles and pistols, so their makers are true. Null means unestablished. |
| `data[].parentId` | string \| null | The manufacturer that owns this one today. |
| `data[].predecessorId` | string \| null | The manufacturer this one succeeded, which is not the same as being owned by it: U.S. Repeating Arms followed Winchester without Winchester owning it. |
| `data[].website` | string \| null | Official website, where one is known. |
| `data[].logoUrl` | string \| null | Absolute URL of the logo, or null where none is on file. |
| `data[].description` | string \| null | Prose summary of the record, where one has been written. |
| `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. |
| `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",
      "name": "Glock Ges.m.b.H.",
      "countryCode": "AT",
      "foundedYear": 1963,
      "status": "active",
      "defunctYear": null,
      "stateOwned": false,
      "makesFirearms": true,
      "parentId": null,
      "predecessorId": null,
      "website": "https://www.glock.com",
      "logoUrl": null,
      "description": null,
      "createdAt": "2025-01-15 12:00:00",
      "updatedAt": "2026-09-10 06:42:19",
      "version": "a3f1c2d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "per_page": 20,
    "total": 939,
    "totalPages": 47
  }
}
```

### 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/manufacturers?country=AT' \
  --header 'X-API-Key: your_key'
```

## client.manufacturers.listAutoPaging()

`GET /v1/manufacturers`

Auth: API key required  
Tier: Explorer+

Auto-paginate through all manufacturers matching the given filters. Returns an async iterator that fetches pages on demand, yielding individual Manufacturer 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) |
| `country` | string | no | Filter by ISO 3166-1 alpha-2 country code |
| `search` | string | no | Substring match on manufacturer name |
| `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` | Manufacturer[] | One page of results |
| `data[].id` | string | URL-safe slug identifying the manufacturer, e.g. `glock`. |
| `data[].name` | string | Display name, as the maker writes it. |
| `data[].countryCode` | string \| null | ISO 3166-1 alpha-2 code of where the company is based. |
| `data[].foundedYear` | integer \| null | Year the company was founded. |
| `data[].status` | string \| null | Whether the company still trades. Much of this catalog is historical, and null means nobody has established which. |
| `data[].defunctYear` | integer \| null | Year the company ceased trading, where that is established. Null while it still trades, and also null where the closing year is simply unknown, so an absent value is never read as "still trading". |
| `data[].stateOwned` | boolean \| null | Government owned. **Null is not false**: it means nobody has established it, and a consumer treating null as false would report every unchecked company as private. |
| `data[].makesFirearms` | boolean \| null | Builds the products this catalog lists, as against importing, rebranding or customising them. Scope is the catalog rather than a legal definition: air rifles are listed here as rifles and pistols, so their makers are true. Null means unestablished. |
| `data[].parentId` | string \| null | The manufacturer that owns this one today. |
| `data[].predecessorId` | string \| null | The manufacturer this one succeeded, which is not the same as being owned by it: U.S. Repeating Arms followed Winchester without Winchester owning it. |
| `data[].website` | string \| null | Official website, where one is known. |
| `data[].logoUrl` | string \| null | Absolute URL of the logo, or null where none is on file. |
| `data[].description` | string \| null | Prose summary of the record, where one has been written. |
| `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. |
| `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",
      "name": "Glock Ges.m.b.H.",
      "countryCode": "AT",
      "foundedYear": 1963,
      "status": "active",
      "defunctYear": null,
      "stateOwned": false,
      "makesFirearms": true,
      "parentId": null,
      "predecessorId": null,
      "website": "https://www.glock.com",
      "logoUrl": null,
      "description": null,
      "createdAt": "2025-01-15 12:00:00",
      "updatedAt": "2026-09-10 06:42:19",
      "version": "a3f1c2d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "per_page": 20,
    "total": 939,
    "totalPages": 47
  }
}
```

### 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/manufacturers?country=AT' \
  --header 'X-API-Key: your_key'
```

## client.manufacturers.get()

`GET /v1/manufacturers/{id}`

Auth: API key required  
Tier: Explorer+

Get a single manufacturer by its slug or ID.

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Manufacturer 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` | Manufacturer | The response payload |
| `data.id` | string | URL-safe slug identifying the manufacturer, e.g. `glock`. |
| `data.name` | string | Display name, as the maker writes it. |
| `data.countryCode` | string \| null | ISO 3166-1 alpha-2 code of where the company is based. |
| `data.foundedYear` | integer \| null | Year the company was founded. |
| `data.status` | string \| null | Whether the company still trades. Much of this catalog is historical, and null means nobody has established which. |
| `data.defunctYear` | integer \| null | Year the company ceased trading, where that is established. Null while it still trades, and also null where the closing year is simply unknown, so an absent value is never read as "still trading". |
| `data.stateOwned` | boolean \| null | Government owned. **Null is not false**: it means nobody has established it, and a consumer treating null as false would report every unchecked company as private. |
| `data.makesFirearms` | boolean \| null | Builds the products this catalog lists, as against importing, rebranding or customising them. Scope is the catalog rather than a legal definition: air rifles are listed here as rifles and pistols, so their makers are true. Null means unestablished. |
| `data.parentId` | string \| null | The manufacturer that owns this one today. |
| `data.predecessorId` | string \| null | The manufacturer this one succeeded, which is not the same as being owned by it: U.S. Repeating Arms followed Winchester without Winchester owning it. |
| `data.website` | string \| null | Official website, where one is known. |
| `data.logoUrl` | string \| null | Absolute URL of the logo, or null where none is on file. |
| `data.description` | string \| null | Prose summary of the record, where one has been written. |
| `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. |

```json
{
  "success": true,
  "data": {
    "id": "glock",
    "name": "Glock Ges.m.b.H.",
    "countryCode": "AT",
    "foundedYear": 1963,
    "status": "active",
    "defunctYear": null,
    "stateOwned": false,
    "makesFirearms": true,
    "parentId": null,
    "predecessorId": null,
    "website": "https://www.glock.com",
    "logoUrl": null,
    "description": null,
    "createdAt": "2025-01-15 12:00:00",
    "updatedAt": "2026-09-10 06:42:19",
    "version": "a3f1c2d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2"
  }
}
```

### 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/manufacturers/glock' \
  --header 'X-API-Key: your_key'
```

## client.manufacturers.getFirearms()

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

Auth: API key required  
Tier: Explorer+

Get all firearms produced by a manufacturer.

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Manufacturer 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` | ManufacturerFirearmListItem[] | 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[].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",
      "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": 939,
    "totalPages": 47
  }
}
```

### 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/manufacturers/glock/firearms' \
  --header 'X-API-Key: your_key'
```

## client.manufacturers.getTimeline()

`GET /v1/manufacturers/{id}/timeline`

Auth: API key required  
Tier: Studio+

Get a chronological timeline for a manufacturer.

### Path parameters

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

### Response 200 (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `data` | object | The response payload. |
| `data.manufacturer` | object | Who the timeline is for. |
| `data.manufacturer.id` | string | Slug of the manufacturer. |
| `data.manufacturer.name` | string | Display name, as the maker writes it. |
| `data.timeline` | object[] | One entry per year they introduced something, oldest first. |
| `data.timeline[].year` | integer \| null | Year of introduction. Null groups the firearms whose year is unrecorded. |
| `data.timeline[].firearms` | object[] | The firearms they introduced that year. |

```json
{
  "success": true,
  "data": {
    "manufacturer": {
      "id": "glock",
      "name": "Glock Ges.m.b.H."
    },
    "timeline": [
      {
        "year": 1982,
        "firearms": [
          {
            "id": "glock-g17-gen5",
            "name": "Glock 17 Gen5",
            "yearIntroduced": 1982,
            "categoryId": "pistol",
            "status": "in_production"
          }
        ]
      }
    ]
  }
}
```

### 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/manufacturers/glock/timeline' \
  --header 'X-API-Key: your_key'
```

## client.manufacturers.getStats()

`GET /v1/manufacturers/{id}/stats`

Auth: API key required  
Tier: Builder+

Get aggregate statistics for a manufacturer.

### Path parameters

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

### Response 200 (application/json)

| Field | Type | Description |
| --- | --- | --- |
| `data` | object | The response payload. |
| `data.manufacturer` | object | Who the figures are about. |
| `data.manufacturer.id` | string | Slug of the manufacturer. |
| `data.manufacturer.name` | string | Display name, as the maker writes it. |
| `data.stats` | object | The aggregate figures. snake_case, as the rollup query names them. |
| `data.stats.total_firearms` | integer | How many catalogued firearms they make. |
| `data.stats.avg_weight_g` | number \| null | Mean unloaded weight in grams across their catalog, over the firearms that record one. |
| `data.stats.avg_range_m` | number \| null | Mean effective range in metres, over the firearms that record one. |
| `data.stats.avg_capacity` | number \| null | Mean magazine capacity, over the firearms that record one. |
| `data.stats.active_count` | integer | How many of their firearms are still in production. |
| `data.stats.discontinued_count` | integer | How many of their firearms are discontinued. |
| `data.stats.earliest_year` | integer \| null | Introduction year of their earliest catalogued firearm. Null when none records one. |
| `data.stats.latest_year` | integer \| null | Introduction year of their most recent catalogued firearm. Null when none records one. |
| `data.categories` | object[] | Categories they build in, most first. |
| `data.categories[].id` | string | Slug of the category. |
| `data.categories[].name` | string | Display name, as the maker writes it. |
| `data.categories[].count` | integer | How many of their firearms are in it. |
| `data.mostCommonCaliber` | object | The single cartridge most of their catalog chambers. Null where none of their firearms records a caliber. |
| `data.mostCommonCaliber.id` | string | Slug of the cartridge. |
| `data.mostCommonCaliber.name` | string | Display name, as the maker writes it. |
| `data.mostCommonCaliber.count` | integer | How many of their firearms chamber it. |

```json
{
  "success": true,
  "data": {
    "manufacturer": {
      "id": "glock",
      "name": "Glock Ges.m.b.H."
    },
    "stats": {
      "total_firearms": 134,
      "avg_weight_g": 669.59,
      "avg_range_m": 48.32,
      "avg_capacity": 13.31,
      "active_count": 97,
      "discontinued_count": 37,
      "earliest_year": 1982,
      "latest_year": 2026
    },
    "categories": [
      {
        "id": "pistol",
        "name": "Pistol",
        "count": 134
      }
    ],
    "mostCommonCaliber": {
      "id": "9x19mm-parabellum",
      "name": "9x19mm Parabellum",
      "count": 63
    }
  }
}
```

### 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/manufacturers/glock/stats' \
  --header 'X-API-Key: your_key'
```
