## GET /openapi/v1/earn/history

One crypto asset's earn products and their daily APY ranges over time, for every platform or for
one.

### Query parameters

| Parameter | Type | Default | Notes |
|:--|:--|:--|:--|
| `id` | integer | required | The asset id from `/assets`. An RWA id is a `404` here, as on `/earn` |
| `provider_id` | string | all platforms | One platform's `provider_id` from `/earn`: a positive whole number, sent as text. Anything else is a `400`; a well-formed id with no products is an empty `products` |
| `interval` | string | `1d` | `1d` only: earn has daily points, so `1h` is a `400` |
| `time_start`, `time_end` | string | none, now | See [History: what the endpoints share](#history-what-the-endpoints-share) |

### Response

```json
{
  "code": 0,
  "message": "success",
  "message_key": "SUCCESS",
  "result": {
    "id": 1,
    "products": [
      {
        "provider_id": "1001",
        "yield_type": "flexible",
        "name": "BTC Flexible",
        "points": [
          { "time": "2026-10-18T00:00:00Z", "apy_min": "0.5", "apy_max": "0.5" },
          { "time": "2026-10-19T00:00:00Z", "apy_min": "0.6", "apy_max": "0.6" }
        ]
      }
    ]
  }
}
```

#### Fields

APYs are percentages as strings: `"3.2"` is 3.2%.

| Field | Type | Notes |
|:--|:--|:--|
| `id` | number | The asset id you asked for. There is no `interval`: it is always `1d` |
| `products` | array | `[]` when the asset has no earn history in the range — not a `404` |
| `products[].provider_id` | string | The platform's id as on `/earn` |
| `products[].yield_type`, `products[].name` | string | A product is its platform, yield type and name together: a product that is renamed starts a new series |
| `products[].points[].time` | string | Midnight UTC of the day, RFC3339 |
| `products[].points[].apy_min`, `products[].points[].apy_max` | string | The product's APY range at the last time we read it that day |

**Order.** Products come by `provider_id` as a number, then by `yield_type` and `name`, each
compared exactly (capital letters before small ones). Each product's points are oldest first.

**The cap counts every product.** The 1,000 points of [the shared rules](#history-what-the-endpoints-share)
are all products' points together, so a long range over many products can be a `400` where one
platform's would not; narrow it with `provider_id` or a shorter range. Without `time_start` you
get the newest whole days that fit.

A day appears once it is over, and can still be revised for a while after. Where our earn history starts is in
[Data methodology](#history); days we missed are not filled in.

### Examples

```bash
# Every platform's earn history for Bitcoin
curl -H "X-Api-Key: <your-api-key>" \
  "https://open-api.coinworldcap.com/openapi/v1/earn/history?id=1"

# One platform's, since 20 October
curl -H "X-Api-Key: <your-api-key>" \
  "https://open-api.coinworldcap.com/openapi/v1/earn/history?id=1&provider_id=1001&time_start=2026-10-20T00:00:00Z"
```

| HTTP status | `code` | When |
|:--|:--|:--|
| 400 | 40000 | `id` missing or not a positive integer; `provider_id` not a positive whole number; `interval` other than `1d`; a parameter the shared history rules refuse, the cap included |
| 404 | 40400 | No crypto asset with that `id` — it does not exist, or it is an RWA's id |

The other errors are in [Errors](#errors).
