> ## Documentation Index
> Fetch the complete documentation index at: https://docs.refindie.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Read API

> Retrieve your programs, affiliates, monthly clicks and sales with a secret read-only API key

# Read API

The Refindie Read API lets you pull your affiliate data into your own tools (CRM, BI, accounting, spreadsheets). It is **read-only**: it cannot create, modify or delete anything.

You only get access to the data you can already see in your Refindie dashboard: your own programs, their affiliates, clicks and sales. Data from other creators is never accessible.

## Authentication

### 1. Create an API key

1. In your creator dashboard, open **API** in the sidebar (or go to **Account Settings → API**).
2. Give the key a name (e.g. `CRM sync`) and click **Create API key**.
3. Copy the key immediately — it starts with `rfd_sk_` and is **shown only once**. Refindie only stores a hash of it.

You can have up to 5 active keys. Revoke a key at any time from the same page; it stops working immediately.

> [!WARNING]
>
> Read API keys give access to personal data (affiliate and customer emails). Keep them on your server only — never in front-end code, a mobile app or a public repository. This key is different from the `X-Api-Key` used by the [sales tracking API](https://docs.refindie.com/setup-affiliationhub.md), which cannot read data.

### 2. Send the key with every request

```bash theme={null}
curl https://www.refindie.com/api/v1/programs \
  -H "Authorization: Bearer rfd_sk_YOUR_KEY"
```

**Base URL:** `https://www.refindie.com/api/v1`

An active Refindie subscription is required, like for the dashboard.

## Endpoints

| Method | Path | Description |
| - | - | - |
| GET | `/programs` | List your affiliate programs |
| GET | `/programs/{programId}/affiliates` | Affiliates with monthly clicks and sales |
| GET | `/programs/{programId}/affiliates/{affiliateId}` | One affiliate with monthly clicks and sales |
| GET | `/programs/{programId}/sales` | Sales with date, amount and commission |

### List programs

`GET /programs`

```json theme={null}
{
  "data": [
    {
      "id": 42,
      "title": "My SaaS affiliate program",
      "status": "active",
      "currency_code": "EUR",
      "created_at": "2026-01-12T09:30:00+00:00"
    }
  ]
}
```

Use the `id` as `programId` in the other endpoints.

### List affiliates

`GET /programs/{programId}/affiliates`

| Query parameter | Type | Default | Description |
| - | - | - | - |
| `from` | `YYYY-MM-DD` | First day of the month, 11 months before `to` | Start of the stats period (UTC, inclusive) |
| `to` | `YYYY-MM-DD` | Today | End of the stats period (UTC, inclusive) |
| `status` | string | all | `active`, `inactive`, `pending` or `rejected` |
| `page` | integer | `1` | Page number |
| `per_page` | integer | `50` | Results per page (max `100`) |

The stats period can cover at most 24 months. Every month in the period is returned, including months with no activity.

```bash theme={null}
curl "https://www.refindie.com/api/v1/programs/42/affiliates?from=2026-08-01&to=2026-09-30" \
  -H "Authorization: Bearer rfd_sk_YOUR_KEY"
```

```json theme={null}
{
  "data": [
    {
      "id": "3f6c1a52-8b1e-4d0a-9a43-0c5e2b7d9f11",
      "email": "jane@example.com",
      "first_name": "Jane",
      "last_name": "Doe",
      "status": "active",
      "registered_at": "2026-03-04T10:15:00.000Z",
      "totals": {
        "clicks": 318,
        "sales_count": 7,
        "sales": [{ "currency": "EUR", "amount": 693, "commission": 207.9 }]
      },
      "monthly": [
        {
          "month": "2026-08",
          "clicks": 140,
          "sales_count": 3,
          "sales": [{ "currency": "EUR", "amount": 297, "commission": 89.1 }]
        },
        {
          "month": "2026-09",
          "clicks": 178,
          "sales_count": 4,
          "sales": [{ "currency": "EUR", "amount": 396, "commission": 118.8 }]
        }
      ]
    }
  ],
  "period": {
    "from": "2026-08-01T00:00:00.000Z",
    "to": "2026-09-30T23:59:59.999Z"
  },
  "pagination": { "page": 1, "per_page": 50, "total": 1, "has_more": false }
}
```

**Affiliate fields**

| Field | Description |
| - | - |
| `id` | Affiliate identifier (UUID), usable with `/affiliates/{affiliateId}` and `/sales?affiliate_id=` |
| `email`, `first_name`, `last_name` | Affiliate identity |
| `status` | Status in your program |
| `registered_at` | Date the affiliate joined your program |
| `totals` | Clicks, number of sales, sales amount and commission over the period |
| `monthly[].month` | Month (`YYYY-MM`, UTC) |
| `monthly[].clicks` | Clicks on the affiliate's links during the month |
| `monthly[].sales_count` | Number of sales attributed to the affiliate |
| `monthly[].sales` | Sales amount and commission, **grouped by currency** (sales are never converted) |

### Get one affiliate

`GET /programs/{programId}/affiliates/{affiliateId}`

Accepts the same `from` / `to` parameters and returns a single affiliate object (same shape as above) in `data`, plus `period`.

### List sales

`GET /programs/{programId}/sales`

| Query parameter | Type | Default | Description |
| - | - | - | - |
| `affiliate_id` | UUID | all | Only sales of this affiliate |
| `from` | `YYYY-MM-DD` | no limit | Sales on or after this date (UTC) |
| `to` | `YYYY-MM-DD` | today | Sales on or before this date (UTC) |
| `page` | integer | `1` | Page number |
| `per_page` | integer | `50` | Results per page (max `100`) |

Sales are sorted from newest to oldest.

```json theme={null}
{
  "data": [
    {
      "id": 1287,
      "date": "2026-09-28T14:02:11.000Z",
      "status": "pending",
      "amount": 99,
      "commission": 29.7,
      "currency": "EUR",
      "customer_email": "customer@example.com",
      "transaction_id": "in_1Q2w3E4r5T6y",
      "affiliate": {
        "id": "3f6c1a52-8b1e-4d0a-9a43-0c5e2b7d9f11",
        "email": "jane@example.com",
        "first_name": "Jane",
        "last_name": "Doe"
      }
    }
  ],
  "pagination": { "page": 1, "per_page": 50, "total": 7, "has_more": false }
}
```

| Field | Description |
| - | - |
| `date` | Date of the sale (ISO 8601, UTC) |
| `amount` | Total amount of the sale, in `currency` |
| `commission` | Commission earned by the affiliate, in `currency` |
| `status` | Commission status (e.g. `pending`, `paid`) |
| `customer_email` | Customer email, when known |
| `transaction_id` | Stripe invoice ID or the `transaction_id` you sent to the sales API |

## Pagination

List endpoints return a `pagination` object. Keep requesting `page + 1` while `has_more` is `true`.

```javascript theme={null}
async function fetchAllSales(programId) {
  const sales = [];
  for (let page = 1; ; page++) {
    const res = await fetch(
      `https://www.refindie.com/api/v1/programs/${programId}/sales?per_page=100&page=${page}`,
      { headers: { Authorization: `Bearer ${process.env.REFINDIE_API_KEY}` } }
    );
    if (!res.ok) throw new Error(`Refindie API error ${res.status}`);
    const body = await res.json();
    sales.push(...body.data);
    if (!body.pagination.has_more) return sales;
  }
}
```

## Errors

Errors use a standard HTTP status and a JSON body:

```json theme={null}
{ "error": { "code": "invalid_api_key", "message": "Invalid or revoked API key." } }
```

| Status | Code | Meaning |
| - | - | - |
| 400 | `invalid_parameter` | A path or query parameter is invalid |
| 401 | `missing_api_key` | No `Authorization: Bearer` header |
| 401 | `invalid_api_key` | Unknown or revoked key |
| 403 | `subscription_required` | No active Refindie subscription |
| 404 | `not_found` | Program or affiliate not found (or not yours) |
| 429 | `rate_limited` | Too many requests — wait for `Retry-After` seconds |
| 500 | `internal_error` | Unexpected error — retry later |

## Rate limits

Each key can make **60 requests per minute**. Every response includes `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (Unix timestamp) headers.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.