# GET /partner/mint/batch/status/{job_id}

Reads the progress of a batch of products sent for minting, from the identifier returned at the moment of the submission.

Source: https://docs.sealtrust.io/en/reference/get-partner-mint-batch-status/

---

You follow the progress of a batch sent for minting, and you know when to stop
your loop. Poll this endpoint as many times as needed: it neither creates nor
modifies any product, and it consumes no daily quota. Read the warning below
before concluding, from this read, that your items exist.

> [!DANGER] No value of `status` tells you how many items were created
> The `status` field describes how the processing went. What was created is read
> in the counters, present from your first call and accurate as of the moment of
> your read. `finished` only says that the batch went all the way through.
> `failed` only says that the processing stopped on a failure, and rows may have
> succeeded all the same: they stay counted, and those items really do exist.
> Read `success_count` before concluding, either way, and before throwing a
> batch away.

The server exposes two addresses that call the same code:
`/v1/partner/mint/batch/status/{job_id}` and
`/partner/mint/batch/status/{job_id}`. Use the `/v1` form for any new
integration.

## Authorization

Scope required on the key: `mint:batch`.

It is the same scope as the submission of a batch. A key that does not have that
scope receives 403, and the message names the missing scope.

Authenticate through the `Authorization` header, in the `Bearer` format.

```http
Authorization: Bearer votre_clef
```

Only a key of the brand that submitted the batch can read it.

## Rate limit

The rate limit of the partner API applies to this endpoint, over a fixed window
of 60 seconds.

Two counters run at the same time, one for your key, one for the sum of the keys
of your brand. The limit applied comes from a value set on your account, or
failing that from your plan. When neither of the two is defined, it is 120 calls
per window.

Do not guess that value. Every response that has cleared the rate check carries
four headers that give it. A 401 response, an authentication 403 or a 503 arrive
before or during that check, and do not carry them.

| Header | What it contains |
| --- | --- |
| `X-RateLimit-Limit` | the applicable limit, in calls per window |
| `X-RateLimit-Remaining` | what is left to you in the current window |
| `X-RateLimit-Reset` | the timestamp, in seconds since 1970, at which the window starts over |
| `X-RateLimit-Scope` | `key` or `brand`, depending on which of the two counters is the more constraining |

> [!ATTENTION] Space out your polls
> A loop that polls this endpoint without a pause exhausts your window in a few
> seconds, and your other calls are refused along with it. Wait a few seconds
> between two reads, and steer your pace on `X-RateLimit-Remaining`.

This endpoint consumes no unit of your daily quota, and no unit of the monthly
product quota of your plan.

## Path and query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `job_id` | `string` | yes | The batch identifier returned by the submission of the batch, in the `job_id` field of its response. Put it in the path of the address. |

This endpoint accepts no query parameter.

## Request body

None. This is a read, it has no body.

## Example request

:::onglets
```bash title="curl"
curl -sS https://api.sealtrust.io/v1/partner/mint/batch/status/0000a1b2c3d4e5f6 \
  -H "Authorization: Bearer st_test_0000000000000000000000000000000000000000000000"
```
```typescript
import { SealTrustClient } from "@sealtrust-io/sdk";

const sealtrust = new SealTrustClient({
  apiKey: "st_test_0000000000000000000000000000000000000000000000",
});

const etat = await sealtrust.products.getBatchStatus("0000a1b2c3d4e5f6");

console.log(etat.status);
console.log(etat.is_finished);
```
```python
import requests

response = requests.get(
    "https://api.sealtrust.io/v1/partner/mint/batch/status/0000a1b2c3d4e5f6",
    headers={
        "Authorization": "Bearer st_test_0000000000000000000000000000000000000000000000"
    },
    timeout=30,
)

print(response.status_code)
print(response.json()["status"])
```
:::

## Example response

HTTP status code 200, for a batch of 500 rows being processed, twelve rows of
which have already succeeded.

```json title="200 OK"
{
  "job_id": "0000a1b2c3d4e5f6",
  "status": "started",
  "is_finished": false,
  "batch_status": "pending",
  "items_count": 500,
  "success_count": 12,
  "error_count": 0,
  "enqueued_at": "2026-08-20T09:14:32.118431+00:00",
  "started_at": "2026-08-20T09:14:33.402118+00:00",
  "ended_at": null,
  "result": null,
  "exc_info": null
}
```

The twelve keys are always present in this shape of response. Only their values
change. The counters are there from your first call: each row of the batch is
counted at the moment it succeeds. When the processing of the batch reaches its
end, `is_finished` turns true and `ended_at` carries a date. That tells you that the processing went
all the way through. To know how many items were created, read `success_count`.

| Field | Type | Description |
| --- | --- | --- |
| `job_id` | `string` | The identifier you asked for, taken up as it is. |
| `status` | `string` | The progress of the processing of the batch. The values are detailed below. |
| `is_finished` | `boolean` | True when the processing of the batch is over. A batch that answers `status: "failed"` can carry either value. Steer your loop on `status`. |
| `batch_status` | `string or null` | The state of the batch itself. The values are detailed below. |
| `items_count` | `integer or null` | The number of rows of the batch. |
| `success_count` | `integer or null` | The number of items actually created as of the moment of your read, counted row by row. |
| `error_count` | `integer or null` | The number of rows actually failed as of the moment of your read, counted row by row. |
| `enqueued_at` | `string or null` | Date and time at which the batch was accepted and queued for processing. |
| `started_at` | `string or null` | Date and time of the start of the processing. Null as long as it has not started. |
| `ended_at` | `string or null` | Date and time of the end of the processing. Null as long as it is not over. |

The three dates carry the `+00:00` offset: they follow universal time.

The response carries two more fields, `result` and `exc_info`. They are internal
to our processing. Ignore them: do not display them, do not store them, do not
wire any logic to them. Neither of the two tells you how many items were
created.

### The values of `status`

| Value | What it means |
| --- | --- |
| `queued` | the batch is waiting its turn |
| `started` | the batch is being processed |
| `finished` | the batch went all the way through, whatever the number of items created |
| `failed` | the processing stopped on a failure. Rows may have succeeded all the same: read `success_count` |
| `unknown` | no batch reachable with this key matches this identifier |

Values other than these five can appear. The types of the TypeScript SDK declare
four more:
`deferred`, `scheduled`, `stopped` and `canceled`. Write your loop so that it
handles an unexpected value without crashing.

Loop as long as the status is `queued` or `started`. Stop on `finished`, on
`failed` and on `unknown`. Stopping your loop tells you nothing about the number
of items created: that number is read in `success_count`.

### The values of `batch_status`

A batch submitted through this API starts at `pending`. Only its processing
makes it change, to `retrying`, to `pending_multisig`, or to a terminal state,
`success`, `partial` or `failed`. The `running` value does not appear on this
path: do not write a branch for it. `partial` means that only part of the items
were created. Compare `success_count` and `items_count` in that case.

> [!ATTENTION] `retrying` and `pending_multisig` do not close by themselves
> `pending_multisig` means that the batch is waiting for signatures, and
> `retrying` that rows have gone back into retry. On these two values, `status`
> can announce a finished processing while the batch is not. Look at
> `batch_status` and `success_count` before considering your items created.

### The response when no reachable batch matches

When no batch reachable with your key matches the identifier, you receive 200,
with two fields and nothing else.

```json title="200 OK"
{
  "job_id": "0000a1b2c3d4e5f6",
  "status": "unknown"
}
```

> [!ATTENTION] `unknown` stops your loop
> This response carries neither `is_finished` nor the counters. An absent
> `is_finished` therefore means that no batch reachable with this key matches.
> Do not conclude from it that the batch is still running: your loop would never
> stop.

### The second shape of response, on an old batch

We only keep the detail of how the processing went for a time. Past that delay,
the response takes a second shape, built from the state of the batch itself. It
carries the first seven fields and nothing else: neither the three dates, nor
the two internal fields. The counters, for their part, are in both shapes.
Handle both shapes.

```json title="200 OK"
{
  "job_id": "0000a1b2c3d4e5f6",
  "status": "failed",
  "is_finished": true,
  "batch_status": "failed",
  "items_count": 500,
  "success_count": 0,
  "error_count": 500
}
```

It can carry any status, including `queued` and `started`. Do not deduce from
its shape that the batch is over.

In this shape of response, `is_finished` is true for `success`, `partial` and
`failed`, and `status` comes from the state of the batch. A batch every row of
which was rejected answers `status: "failed"` and `is_finished: true` here, and
the first shape answers the same thing for the same run. Do not compare two
reads made at different moments to deduce that the batch has changed state. The
number of items created is read in `success_count`.

## Errors

| Code | Condition | What to do |
| --- | --- | --- |
| 401 | the `Authorization` header is absent | add it, the response also carries `WWW-Authenticate: Bearer` |
| 401 | the header does not start with `Bearer ` followed by a space | correct the shape of the header, the response also carries `WWW-Authenticate: Bearer` |
| 401 | the value sent after `Bearer ` is shorter than 40 characters | send the complete secret, this response does not carry `WWW-Authenticate` |
| 401 | the value sent is not a known key | check that you are copying the whole secret, without a space or a line break |
| 403 | the key has been revoked, the message gives its state | create a new key from the console of your brand |
| 403 | the key has reached its expiry date | create a new key, the old one will never become valid again |
| 403 | the key does not have the `mint:batch` scope, the message names it | create a key carrying that scope, it also governs the submission of a batch |
| 403 | you do not have access to this batch | check that you are polling the right identifier with the key of the right brand |
| 429 | your key has gone over its own rate limit | wait the number of seconds indicated by `Retry-After`, then try again. `X-RateLimit-Scope` is then `key` |
| 429 | all the keys of your brand together have gone over the limit of the brand | wait the number of seconds indicated by `Retry-After`, then try again. `X-RateLimit-Scope` is then `brand`. Adding keys does not raise that limit |
| 500 | an unexpected internal error | try again. If the refusal repeats, write to us giving the value of the `X-Request-Id` header of the response |
| 503 | the service that holds the rate counters is momentarily unavailable | the call read nothing. Try again later |

No error code in this list depends on the content of the batch. The check for
the refusals tied to the batch itself, size, columns, categories and the quota
of your plan, takes place at the moment of the submission, on
`POST /v1/partner/mint/batch`.

## See also

- [`POST /partner/mint/batch`](/en/reference/post-partner-mint-batch/),
  submit a batch of product rows, up to 500 per call.
- [Create products, one at a time and in batches](/en/creer-des-produits/),
  create a product, create a whole run, import a file.
- [API errors](/en/api-erreurs/),
  recognize an error code and decide whether to fix or replay.
- [Integrate the TypeScript SDK](/en/sdk-typescript/),
  install the SDK, create the client and recognize the two families of errors.
