## GET /openapi/v1/earn

The earn products — flexible, locked and other yield products — that platforms offer for one
crypto asset, grouped by platform, with their APYs. A product whose APY is unusually high is
flagged as a promotion and kept out of its platform's APY range.

### Query parameters

| Parameter | Type | Notes |
|:--|:--|:--|
| `id` | integer | **Required.** The asset id from `/assets`. An RWA id is not an asset id and is a `404` here |

### Response

```json
{
  "code": 0,
  "message": "success",
  "message_key": "SUCCESS",
  "result": {
    "id": 1,
    "data_time": "2026-10-20T08:15:00Z",
    "promo_threshold": "100",
    "providers": [
      {
        "provider_id": "1001",
        "provider_name": "Binance",
        "exchange_slug": "binance",
        "ecosystem": "cefi",
        "yield_types": ["flexible", "locked"],
        "apy_min": "0.5",
        "apy_max": "3.2",
        "products": [
          {
            "yield_type": "flexible",
            "name": "BTC Flexible",
            "apy_min": "0.5",
            "apy_max": "0.5",
            "is_promo": false,
            "subscribe_url": "https://www.binance.com/en/earn"
          },
          {
            "yield_type": "locked",
            "name": "BTC Locked 90D",
            "apy_min": "1",
            "apy_max": "3.2",
            "is_promo": false,
            "subscribe_url": ""
          }
        ]
      }
    ]
  }
}
```

#### Fields

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

| Field | Type | Notes |
|:--|:--|:--|
| `id` | number | The asset id you asked for |
| `data_time` | string or `null` | When the products were collected, RFC3339 UTC. `null` when we hold no data for this asset, or hold it without a collection time |
| `promo_threshold` | string | A product whose `apy_max` is **strictly above** this is a promotion. Currently `"100"` |
| `providers` | array | One item per platform. `[]` when the asset has no earn products — not a `404` |
| `providers[].provider_id` | string | The platform's id as CoinMarketCap assigns it — **not** an exchange id from `/exchanges`, and a string for that reason |
| `providers[].provider_name` | string | |
| `providers[].exchange_slug` | string or `null` | The platform's exchange slug as our data source gives it, or `null`. It is not guaranteed to name an exchange on `/exchanges` |
| `providers[].ecosystem` | string or `null` | |
| `providers[].yield_types` | array of strings | The kinds of yield the platform offers, each once |
| `providers[].apy_min`, `providers[].apy_max` | string or `null` | The range over the platform's **non-promotional** products. `null` when all of them are promotions |
| `providers[].products` | array | |
| `providers[].products[].yield_type`, `providers[].products[].name` | string | |
| `providers[].products[].apy_min`, `providers[].products[].apy_max` | string | |
| `providers[].products[].is_promo` | boolean | `apy_max` above `promo_threshold` |
| `providers[].products[].subscribe_url` | string | Where to subscribe: an `https` link without its query and fragment. **Empty string** when there is none |

**Order.** Platforms come by their `apy_max`, highest first; those whose products are all
promotions come last; platforms with the same `apy_max` keep the order we collected them in.
Products keep that order within their platform.

### Examples

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

| HTTP status | `code` | When |
|:--|:--|:--|
| 400 | 40000 | `id` missing, or not a positive integer |
| 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).
