# Get Data Source List

Source: https://help.zira.us/developers/api-reference/get-data-source-list
Summary: Browse the data sources the API key can see, a page at a time.
Updated: 2026-08-06

## `GET /data-sources`

Use this endpoint to discover data sources when you do not already know their IDs. Results are paged with a cursor and can be filtered by site, usage or name. Once you have the IDs, `GET /data-sources/{id}` gives you the full record.

## Authentication

```http
x-api-key: <your-api-key>
```

See [API Authentication](/developers/api-reference/api-authentication) for how to create a key.

## Query parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | number | no | Page size. Always send it, so that a missing `lastValue` reliably means you have reached the end. |
| `lastValue` | string | no | Cursor for the next page. Pass the `lastValue` from the previous response back verbatim. |
| `siteIds` | string | no | Comma-separated site IDs to restrict the list to, e.g. `1512,1513`. |
| `meterIds` | string | no | Comma-separated data source IDs, when you want a known set rather than a browse. |
| `applyingIds` | string | no | Comma-separated usage IDs — `1` Sensor, `2` form, `7` AI Vision. |
| `textSearch` | string | no | Free-text search over data source names. Keep sending the same value while paging through its results. |
| `withLastReadings` | boolean | no | Adds each row’s most recent reading. Any value at all turns this on — omit the parameter to leave it off. |
| `withDetails` | boolean | no | Expands each row to all of its metrics. Any value at all turns this on — omit the parameter to leave it off. |
| `onlyMain` | boolean | no | Send the literal string `true` to restrict each row to its main metric. |

## Response

`200 OK`

Returns a page of data sources in `data`, with an opaque `lastValue` cursor beside it. Each row is a summary — enough to identify a data source and decide which ones you care about. Fetch the full record with `GET /data-sources/{id}`.

```json
{
  "data": [
    {
      "id": "43510",
      "name": "Line 1 Label Counter",
      "validationStatus": 20,
      "type": {
        "id": "50",
        "name": "Form",
        "ordinal": 1
      },
      "site": {
        "id": "1512",
        "name": "Fresno Plant",
        "channelId": "45187"
      },
      "applying": {
        "id": "7",
        "name": "AI Vision",
        "channelId": null
      },
      "channelId": "67363",
      "isShared": false
    }
  ],
  "lastValue": "eyJ0eXBlT3JkaW5hbCI6MSwibmFtZSI6IkxpbmUgMSBMYWJlbCBDb3VudGVyIiwiaWQiOiI0MzUxMCJ9"
}
```

| Field | Type | Description |
| --- | --- | --- |
| `data` | array (nullable) | A page of data sources. `null` — not an empty array — when nothing matched. |
| `data[].id` | string | Data source ID — use it with `GET /data-sources/{id}`, `GET /reading` and `POST /reading`. |
| `data[].name` | string | Display name. |
| `data[].validationStatus` | number (nullable) | Where the data source is in validation: `10` Pending Validation, `20` Valid, `30` Invalid. A number here, while `GET /data-sources/{id}` returns the same thing as an object. |
| `data[].type` | object | `{ id, name, ordinal }` — what kind of data source it is. |
| `data[].site` | object | `{ id, name, channelId }` for the owning site. |
| `data[].applying` | object (nullable) | `{ id, name }` — how it is used: `1` Sensor, `2` form, `7` AI Vision. |
| `data[].channelId` | string (nullable) | Channel that carries this data source’s feed. |
| `data[].isShared` | boolean | Whether the data source is shared with other sites. |
| `lastValue` | string | Opaque cursor for the next page. Pass it back as `?lastValue=…`. |

### Good to know

- Rows always carry `id`, `name`, `validationStatus`, `type`, `site` and `isShared`. Other fields depend on the data source and on the flags you send, so read defensively.
- Add `withLastReadings` to get each row’s latest reading inline, or `withDetails` to expand every metric — either saves a follow-up call per data source.
- Do not treat the presence of `lastValue` as proof that more data exists. Keep paging until `data` comes back `null`, or until you receive fewer rows than the `limit` you asked for.
- The list is ordered by type, then name, then ID, and the cursor is built from those three. There is no way to change the sort.
- An empty result is `{"data": null}`, not `{"data": []}`.
- To map metric IDs to names for a data source, call `GET /data-sources/{id}?withSchema=true`.

## Errors

Failures return a JSON body with a `message` and an `internalErrorCode`, at the status shown below. Whenever the underlying error carries structured detail — which every schema-validation failure does — the body is replaced with a generic `Internal server error.` / `02-001` pair while the real status code is kept. Branch on the HTTP status and on `internalErrorCode`; never parse the message text.

```json
{
  "message": "Internal server error.",
  "internalErrorCode": "02-001"
}
```

| Status | Meaning |
| --- | --- |
| `400` | The request failed validation — a missing required field, a field the schema does not allow, an unparseable body, a value out of range, or a malformed `lastValue`. |
| `401` | The API key resolved to a user context that is not allowed to read or write the record you asked for. |
| `403` | The `x-api-key` header is missing or is not a recognised key. API Gateway rejects the call before the endpoint runs, so the body is only `{"message":"Forbidden"}`. |
| `409` | The write conflicts with an existing record, or it references a record that does not exist. |
| `429` | The API key exceeded the rate limit or quota on its usage plan. Also returned by API Gateway rather than the endpoint. |
| `500` | Unexpected server error, or the database returned no result where one was required. |
