# HTTP — `GET /v1/points`

The venue points that a user earned, per venue and per recorded period. Public
endpoint. Some venues give points to the Mosaiq account for the trading of its
clients. Mosaiq keeps a share of these points and splits the rest between its
clients in proportion to their fees. [Points](/concepts/points) gives the rule.
These are not the `points` of
[`GET /v1/portfolioHistory`](/api/portfolio-history), which hold account
values.

```http
GET /v1/points?user=0x00000000000000000000000000000000000000a1
```

| Param | Required | Meaning |
| --- | --- | --- |
| `user` | yes | EVM address. Missing fails with `invalid_query`; malformed with `bad_request`. |

```jsonc
{
  "status": "ok",
  "data": {
    "user": "0x00000000000000000000000000000000000000a1",
    "venues": [
      {
        "venue": "lighter:robinhood",
        "totalEarnedPoints": "14522.727272",
        "periods": [
          {
            "startMs": 1790294400000,
            "endMs": 1790899200000,
            "revision": 1,
            "recordedAtMs": 1791190800000,
            "venuePoints": "12500",
            "mosaiqShare": "0.1",
            "clientPoolPoints": "11250",
            "clientFeesUsdc": "41.25",
            "feesUsdc": "31.25",
            "earnedPoints": "8522.727272"
          },
          {
            "startMs": 1790899200000,
            "endMs": 1791504000000,
            "revision": 1,
            "recordedAtMs": 1791795600000,
            "venuePoints": "10000",
            "mosaiqShare": "0.1",
            "clientPoolPoints": "9000",
            "clientFeesUsdc": "45",
            "feesUsdc": "30",
            "earnedPoints": "6000"
          }
        ]
      }
    ]
  },
  "server_time_ms": 1792051200000
}
```

In this example, Lighter gave Mosaiq 12,500 points for the week that ends on
Friday 2 October 2026 and 10,000 points for the next week. The user paid 31.25
USDC of the 41.25 USDC of client fees in the first week, and 30 of 45 in the
second week.

| Field | Meaning |
| --- | --- |
| `venues` | One entry per venue with a recorded period, by venue code. Empty when Mosaiq has recorded no period. |
| `venue` | The venue whose points program gave the points. |
| `totalEarnedPoints` | The sum of `earnedPoints` over the periods of the venue. |
| `periods` | Each recorded period of the venue at its latest revision, oldest first. |
| `startMs`, `endMs` | The window of the period in Unix milliseconds. `startMs` is inclusive and `endMs` is exclusive. |
| `revision` | `1` for the first record of the period. A correction gives a higher value. |
| `recordedAtMs` | When Mosaiq recorded this revision, in Unix milliseconds. |
| `venuePoints` | The points that the venue gave Mosaiq for the window. |
| `mosaiqShare` | The share of `venuePoints` that Mosaiq keeps. |
| `clientPoolPoints` | `venuePoints × (1 − mosaiqShare)`, rounded down to 6 decimal places. |
| `clientFeesUsdc` | The sum of `feesUsdc` over all clients in the period. |
| `feesUsdc` | The fees of the user in the period: for each fill of the user on the venue in the window, `max(0, fees.exchange + fees.mosaiq + fees.priority)` of [`GET /v1/userFills`](/api/user-fills), added together and rounded half to even to 6 decimal places. An absent `fees.priority` counts as 0. |
| `earnedPoints` | `clientPoolPoints × feesUsdc / clientFeesUsdc`, rounded down to 6 decimal places. `"0"` when `clientFeesUsdc` is `"0"`. |

Decimals are strings with at most 6 decimal places. Every address gets every
recorded period. An unknown address, an address without fees in a period, and
the Mosaiq fee and House accounts get `"0"` in `feesUsdc` and `earnedPoints`.

A fill is in a period when its `createdAtMs` is in `[startMs, endMs)`. To check
`feesUsdc`, query [`GET /v1/userFills`](/api/user-fills) with
`startTime = startMs` and `endTime = endMs - 1`, and keep the fills of the
venue.

A correction replaces the figures of a period: `revision` increases and
`recordedAtMs` changes. The endpoint shows only the latest revision.

## Errors

| HTTP | Code | Meaning |
| --- | --- | --- |
| `400` | `invalid_query` | `user` is missing. |
| `400` | `bad_request` | `user` is not an EVM address. |
| `429` | `rate_limited` | The request exceeds the HTTP limit. |
| `503` | `public_reads_busy` | The server is busy with other database reads. Retry with backoff. |
| `500` | `internal_error` | The server could not read the points. |

This endpoint reads the database on every request. When all the read slots
are in use, it returns HTTP `503` with code `public_reads_busy`, as
[Busy server](/api/http-public#busy-server) describes.

Requests count toward the shared HTTP limit of
[600 per minute per IP](/api/http-public#rate-limits).
