---
title: "Successful mints on a contract by NimbusMint users from your app"
description: "GET /hits"
canonical: "https://docs.nimbusmint.bot/api/operations/gethits/"
---
> Index: https://docs.nimbusmint.bot/llms.txt

# Successful mints on a contract by NimbusMint users from your app

`GET https://api.nimbusmint.bot/partner/v1/hits`

Counts **hits** on one contract in a window. A hit is one mint task
that completed successfully (receipt confirmed, not reverted) and that
was booked through your app, timed by when it completed. It is not a
token count: a task that minted three tokens is one hit. Failed,
reverted and cancelled tasks never count, and nor do tasks booked
before your app's attribution went live.

Users are distinct NimbusMint accounts. Each listed user carries
their full identity: `user_id`, `discord_id`, `discord_username` and
the `wallets` their hits in the window minted from. These are your
users' personal data: mask them yourself before showing them to
anyone else. `handle` is a ready-masked form (the first character of
their name and seven `*`). `users` is ranked by hits, then by most
recent hit; `others` sums everyone beyond `top`, so `total` = `users`
+ `others`. For every individual hit, use `/mints`.

`gas` is the user's settings on their most recent hit in the window:
`manual` with the max fee and priority fee in gwei, or `auto` (fees
chosen by the network at send time) with nulls.

Answers are cached for `cache_ttl_s` seconds; `since` and `until` are
floored to the minute.

Requires scope `read:hits`.

## Authentication

- `privateKey`: `Authorization: Bearer nmk_live_<id>_<secret>`: `<id>` is 12 characters (the key id shown to you is `nmk_<id>`), `<secret>` 64 hex characters. The full token is shown once, when the key is issued; we store only a hash of it and cannot show it again. The key works only from the addresses on its IP allowlist (public IPv4 /24 or narrower, IPv6 /48 or narrower, at most 16 entries). It expires on its `expires_at` (at most one year after issue), and a revoked key stops working within 10 seconds. Keep it on your servers; never put it in a client.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `contract` | query | string | yes | Contract address, 0x-prefixed, any case. Example `0x000000000000000000000000000000000000dead`. |
| `since` | query | string (date-time) | yes | Window start (inclusive), RFC 3339. Example `2026-10-08T15:00:00Z`. |
| `until` | query | string (date-time) | no | Window end (exclusive), RFC 3339. Defaults to now; a future time is clamped to now. The window may span at most 31 days unless your agreement sets another limit (`window_too_large`). |
| `chain_id` | query | integer | no | Only this chain. Omitted, every chain is counted and `chain_ids` lists those that matched. Minimum 1. Example `33139`. |
| `top` | query | integer | no | How many users to list individually. Default `25`. Range 1 to 50. |

## Responses

### 200

Hits in the window.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `contract` | string | yes | The contract, lowercased. |
| `chain_ids` | integer[] | yes | Chains with at least one hit, ascending. |
| `since` | string (date-time) | yes |  |
| `until` | string (date-time) | yes |  |
| `total` | object | yes |  |
| `total.hits` | integer | yes |  |
| `total.users` | integer | yes | Distinct NimbusMint accounts. |
| `users` | object[] | yes |  |
| `users[].handle` | string | yes | First character of the user's name and seven `*`. Not unique. |
| `users[].user_id` | string | yes | The NimbusMint account id. Stable; today it equals the Discord id. |
| `users[].discord_id` | string | yes | The user's Discord id; empty if the account record is missing. |
| `users[].discord_username` | string | yes | The user's Discord username as last seen at login; empty if unknown. |
| `users[].wallets` | string[] | yes | Distinct wallet addresses (lowercase, ascending) the user's hits in the window minted from. |
| `users[].hits` | integer | yes |  |
| `users[].gas` | object | yes |  |
| `users[].gas.mode` | string | yes | One of: `manual`, `auto`. |
| `users[].gas.max_fee_gwei` | string \| null | yes | Max fee per gas in gwei, decimal string; null when `auto`. |
| `users[].gas.priority_gwei` | string \| null | yes | Priority fee per gas in gwei, decimal string; null when `auto` or not set. |
| `others` | object | yes |  |
| `others.users` | integer | yes |  |
| `others.hits` | integer | yes |  |
| `generated_at` | string (date-time) | yes |  |
| `cache_ttl_s` | integer | yes | Seconds this answer is cached for. |

Example `hits` (A collection with one auto-gas user and a tail):

```json
{
  "contract": "0x000000000000000000000000000000000000dead",
  "chain_ids": [
    33139
  ],
  "since": "2026-10-08T15:00:00Z",
  "until": "2026-10-08T18:42:00Z",
  "total": {
    "hits": 36,
    "users": 11
  },
  "users": [
    {
      "handle": "a*******",
      "user_id": "100000000000000001",
      "discord_id": "100000000000000001",
      "discord_username": "alice",
      "wallets": [
        "0x1111111111111111111111111111111111111111"
      ],
      "hits": 9,
      "gas": {
        "mode": "manual",
        "max_fee_gwei": "3",
        "priority_gwei": "0.5"
      }
    },
    {
      "handle": "b*******",
      "user_id": "100000000000000002",
      "discord_id": "100000000000000002",
      "discord_username": "bob",
      "wallets": [
        "0x2222222222222222222222222222222222222222",
        "0x3333333333333333333333333333333333333333"
      ],
      "hits": 7,
      "gas": {
        "mode": "auto",
        "max_fee_gwei": null,
        "priority_gwei": null
      }
    }
  ],
  "others": {
    "users": 9,
    "hits": 20
  },
  "generated_at": "2026-10-08T18:42:07Z",
  "cache_ttl_s": 30
}
```

### 400

The query is invalid (`invalid_request`) or the window is longer than allowed (`window_too_large`).

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `code` | string | yes | One of: `invalid_key`, `ip_not_allowed`, `scope_missing`, `rate_limited`, `ip_trust_unconfigured`, `tenant_api_disabled`, `invalid_request`, `window_too_large`, `not_found`, `internal`. |
| `message` | string | yes |  |

Example `window`:

```json
{
  "code": "window_too_large",
  "message": "the window may span at most 31 days"
}
```

### 401

The key is missing, malformed, unknown, expired or revoked, or its partner is disabled. Always `invalid_key`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `code` | string | yes | One of: `invalid_key`, `ip_not_allowed`, `scope_missing`, `rate_limited`, `ip_trust_unconfigured`, `tenant_api_disabled`, `invalid_request`, `window_too_large`, `not_found`, `internal`. |
| `message` | string | yes |  |

Example `invalid`:

```json
{
  "code": "invalid_key",
  "message": "missing, invalid, expired or revoked key"
}
```

### 403

The calling address is not on the key's allowlist (`ip_not_allowed`), or the key lacks the route's scope (`scope_missing`).

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `code` | string | yes | One of: `invalid_key`, `ip_not_allowed`, `scope_missing`, `rate_limited`, `ip_trust_unconfigured`, `tenant_api_disabled`, `invalid_request`, `window_too_large`, `not_found`, `internal`. |
| `message` | string | yes |  |

Example `ip`:

```json
{
  "code": "ip_not_allowed",
  "message": "this address may not use this key"
}
```

### 429

The key's rate limit, or the API's overall limit, is exceeded (`rate_limited`).

Headers:

- `Retry-After`: Seconds to wait before retrying.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `code` | string | yes | One of: `invalid_key`, `ip_not_allowed`, `scope_missing`, `rate_limited`, `ip_trust_unconfigured`, `tenant_api_disabled`, `invalid_request`, `window_too_large`, `not_found`, `internal`. |
| `message` | string | yes |  |

Example `limited`:

```json
{
  "code": "rate_limited",
  "message": "rate limit exceeded"
}
```

### 503

The API is switched off (`tenant_api_disabled`), not configured (`ip_trust_unconfigured`) or temporarily unable to answer (`internal`). Retry later; `Retry-After` is sent when a retry is expected to succeed soon.

Headers:

- `Retry-After`: Seconds to wait before retrying.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `code` | string | yes | One of: `invalid_key`, `ip_not_allowed`, `scope_missing`, `rate_limited`, `ip_trust_unconfigured`, `tenant_api_disabled`, `invalid_request`, `window_too_large`, `not_found`, `internal`. |
| `message` | string | yes |  |

Example `disabled`:

```json
{
  "code": "tenant_api_disabled",
  "message": "the partner API is disabled"
}
```

## Errors

| Status | `code` | Meaning |
| --- | --- | --- |
| 400 | `window_too_large` | The query is invalid (`invalid_request`) or the window is longer than allowed (`window_too_large`). |
| 400 | `invalid_request` | The query is invalid (`invalid_request`) or the window is longer than allowed (`window_too_large`). |
| 401 | `invalid_key` | The key is missing, malformed, unknown, expired or revoked, or its partner is disabled. Always `invalid_key`. |
| 403 | `ip_not_allowed` | The calling address is not on the key's allowlist (`ip_not_allowed`), or the key lacks the route's scope (`scope_missing`). |
| 403 | `scope_missing` | The calling address is not on the key's allowlist (`ip_not_allowed`), or the key lacks the route's scope (`scope_missing`). |
| 429 | `rate_limited` | The key's rate limit, or the API's overall limit, is exceeded (`rate_limited`). |
| 503 | `tenant_api_disabled` | The API is switched off (`tenant_api_disabled`), not configured (`ip_trust_unconfigured`) or temporarily unable to answer (`internal`). Retry later; `Retry-After` is sent when a retry is expected to succeed soon. |
| 503 | `ip_trust_unconfigured` | The API is switched off (`tenant_api_disabled`), not configured (`ip_trust_unconfigured`) or temporarily unable to answer (`internal`). Retry later; `Retry-After` is sent when a retry is expected to succeed soon. |
| 503 | `internal` | The API is switched off (`tenant_api_disabled`), not configured (`ip_trust_unconfigured`) or temporarily unable to answer (`internal`). Retry later; `Retry-After` is sent when a retry is expected to succeed soon. |

What to do about each: [Errors](https://docs.nimbusmint.bot/errors.md).

## Example request

### curl

```sh
curl -G "https://api.nimbusmint.bot/partner/v1/hits" \
  -H "Authorization: Bearer $NIMBUS_PRIVATE_KEY" \
  --data-urlencode "contract=0x000000000000000000000000000000000000dead" \
  --data-urlencode "since=2026-10-08T15:00:00Z"
```

### Node

```js
// Node 18+ (built-in fetch). Keep the key on your server.
const url = new URL('https://api.nimbusmint.bot/partner/v1/hits');
url.search = new URLSearchParams({
  contract: '0x000000000000000000000000000000000000dead',
  since: '2026-10-08T15:00:00Z',
});
const res = await fetch(url, {
  headers: { Authorization: `Bearer ${process.env.NIMBUS_PRIVATE_KEY}` },
});
console.log(res.status, await res.json());
```

### Python

```python
import os
import requests

res = requests.get(
    "https://api.nimbusmint.bot/partner/v1/hits",
    headers={"Authorization": f"Bearer {os.environ['NIMBUS_PRIVATE_KEY']}"},
    params={
        "contract": "0x000000000000000000000000000000000000dead",
        "since": "2026-10-08T15:00:00Z",
    },
    timeout=10,
)
print(res.status_code, res.json())
```
