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

# Macro screener

> Screen countries by the latest values of canonical macro indicators with a compact filter DSL: field, operator, value, all AND-ed.

The screener is the cross-country analogue of an equity stock screener. Instead of filtering tickers by fundamentals, you filter countries by the latest values of canonical macro indicators: "show me countries with CPI inflation above 3% and unemployment below 5%." Financial Data API evaluates the latest public reading of each indicator per country and returns the countries that satisfy every clause.

<Note>
  The screener screens **public indicator values only**. It reads the latest public observation per canonical indicator and applies your filters to those numbers. It never exposes any internal scoring, and it never touches licensed or `internal_only` data.
</Note>

## How it works

The HTTP API is read-only and GET-only, so filters arrive as a single compact query parameter, the `filter` DSL. Each clause is `field:operator:value`, clauses are comma-separated, and they are AND-ed together. A country qualifies only when every clause matches one of its indicators and that indicator's latest value satisfies the operator.

```text theme={"theme":"css-variables"}
filter=cpi_inflation_yoy:gt:3,unemployment_rate:lt:5
```

This reads as: CPI inflation greater than 3 **and** unemployment rate less than 5.

## The filter DSL

### Anatomy of a clause

A clause has three colon-separated parts:

| Part       | Example             | Notes                                                                      |
| ---------- | ------------------- | -------------------------------------------------------------------------- |
| `field`    | `cpi_inflation_yoy` | A canonical indicator slug. Discover the full list at `/screener/filters`. |
| `operator` | `gt`                | One of the operators below.                                                |
| `value`    | `3`                 | A number. The `in` operator takes pipe-separated numbers.                  |

### Operators

| Operator | Meaning                          | Example                  |      |       |
| -------- | -------------------------------- | ------------------------ | ---- | ----- |
| `gt`     | Greater than                     | `cpi_inflation_yoy:gt:3` |      |       |
| `lt`     | Less than                        | `unemployment_rate:lt:5` |      |       |
| `gte`    | Greater than or equal            | `policy_rate:gte:4`      |      |       |
| `lte`    | Less than or equal               | `gdp_growth_yoy:lte:1`   |      |       |
| `eq`     | Equal                            | `policy_rate:eq:0`       |      |       |
| `in`     | Value is in a pipe-separated set | \`policy\_rate:in:0      | 0.25 | 0.5\` |

The `in` operator is the only one that takes more than one value. Separate its values with the pipe character `|`:

```text theme={"theme":"css-variables"}
filter=policy_rate:in:0|0.25|0.5
```

### AND semantics

Clauses are joined with commas and every clause must hold. There is no `OR`; build the union you want client-side by issuing separate requests. A country with no reading for a clause's field, or a `null` latest value, does not satisfy that clause and is dropped.

```text theme={"theme":"css-variables"}
# Inflation above 3 AND unemployment below 5 AND policy rate at or above 4
filter=cpi_inflation_yoy:gt:3,unemployment_rate:lt:5,policy_rate:gte:4
```

## Discovering fields

Field names are canonical indicator slugs. The authoritative list, with the operators, the syntax reminder, and an example, lives at `GET /screener/filters`.

<CodeGroup>
  ```bash cURL theme={"theme":"css-variables"}
  curl -s "https://api.financialdatapi.com/screener/filters" \
    -H "x-api-key: $FINANCIALDATA_API_KEY"
  ```

  ```python Python (requests) theme={"theme":"css-variables"}
  import os, requests

  resp = requests.get(
      "https://api.financialdatapi.com/screener/filters",
      headers={"x-api-key": os.environ["FINANCIALDATA_API_KEY"]},
  )
  resp.raise_for_status()
  data = resp.json()["data"]
  print(data["operators"])
  for f in data["fields"]:
      print(f["field"], "-", f["name"])
  ```

  ```typescript TypeScript (@financialdatapi/client) theme={"theme":"css-variables"}
  import { FinancialDataApiClient } from "@financialdatapi/client";

  const client = new FinancialDataApiClient({ apiKey: process.env.FINANCIALDATA_API_KEY });
  const { data } = await client.screenerFields();
  console.log(data.fields);
  ```
</CodeGroup>

The response:

```json theme={"theme":"css-variables"}
{
  "data": {
    "operators": ["gt", "lt", "gte", "lte", "eq", "in"],
    "syntax": "filter=field:operator:value,field:operator:value (clauses are AND-ed; the 'in' operator takes pipe-separated values)",
    "example": "filter=cpi_inflation_yoy:gt:3,unemployment_rate:lt:5",
    "fields": [
      {
        "field": "cpi_inflation_yoy",
        "indicatorId": "indicator_cpi_inflation_yoy",
        "name": "CPI inflation, year over year",
        "category": "inflation",
        "unit": "percent"
      }
    ]
  }
}
```

<Tip>
  Field matching is forgiving: it accepts the canonical slug, the `indicator_`-prefixed id, and registered aliases for the same indicator. When in doubt, use the `field` value exactly as returned by `/screener/filters`.
</Tip>

## Running a screen

Send your clauses as the `filter` parameter to `GET /screener`. The `filter` parameter is required.

<CodeGroup>
  ```bash cURL theme={"theme":"css-variables"}
  curl -s "https://api.financialdatapi.com/screener?filter=cpi_inflation_yoy:gt:3,unemployment_rate:lt:5" \
    -H "x-api-key: $FINANCIALDATA_API_KEY"
  ```

  ```python Python (requests) theme={"theme":"css-variables"}
  import os, requests

  resp = requests.get(
      "https://api.financialdatapi.com/screener",
      headers={"x-api-key": os.environ["FINANCIALDATA_API_KEY"]},
      params={"filter": "cpi_inflation_yoy:gt:3,unemployment_rate:lt:5"},
  )
  resp.raise_for_status()
  for hit in resp.json()["data"]:
      print(hit["country"], hit["name"])
  ```

  ```typescript TypeScript (@financialdatapi/client) theme={"theme":"css-variables"}
  import { FinancialDataApiClient, buildScreenerFilter } from "@financialdatapi/client";

  const client = new FinancialDataApiClient({ apiKey: process.env.FINANCIALDATA_API_KEY });

  // Pass structured filters; the SDK serializes them into the DSL for you.
  const { data } = await client.screen([
    { field: "cpi_inflation_yoy", operator: "gt", value: 3 },
    { field: "unemployment_rate", operator: "lt", value: 5 },
  ]);
  console.log(data);

  // Equivalent with a raw DSL string:
  await client.screen("cpi_inflation_yoy:gt:3,unemployment_rate:lt:5");

  // buildScreenerFilter() is exported if you want the string without calling screen():
  const filter = buildScreenerFilter([
    { field: "policy_rate", operator: "in", value: [0, 0.25, 0.5] },
  ]);
  // filter === "policy_rate:in:0|0.25|0.5"
  ```
</CodeGroup>

### The response

Each result is a country that satisfied every clause, with the matched indicator values inlined so you can see what it scored on without a second call.

```json theme={"theme":"css-variables"}
{
  "data": [
    {
      "entity": "USA",
      "country": "USA",
      "name": "United States",
      "values": [
        {
          "field": "cpi_inflation_yoy",
          "indicatorId": "cpi_inflation_yoy",
          "indicatorName": "CPI inflation, year over year",
          "value": 3.1,
          "unit": "percent",
          "periodEnd": "2026-05-31",
          "observedAt": "2026-05-31"
        },
        {
          "field": "unemployment_rate",
          "indicatorId": "unemployment_rate",
          "indicatorName": "Unemployment rate",
          "value": 4.2,
          "unit": "percent",
          "periodEnd": "2026-05-31",
          "observedAt": "2026-05-31"
        }
      ]
    }
  ],
  "meta": { "api_version": "v1" },
  "requestId": "0c4f..."
}
```

<ResponseField name="entity" type="string">
  The matched entity key (the country ISO code).
</ResponseField>

<ResponseField name="country" type="string">
  ISO alpha-3 country code.
</ResponseField>

<ResponseField name="name" type="string">
  Country display name.
</ResponseField>

<ResponseField name="values" type="object[]">
  One entry per clause, showing the indicator value that satisfied it.
</ResponseField>

## Optional parameters

Besides the required `filter`, the screener accepts three optional parameters.

| Param       | Values                | Description                                                                                    |
| ----------- | --------------------- | ---------------------------------------------------------------------------------------------- |
| `country`   | ISO code              | Restrict the screen to a single country (useful as a yes/no test).                             |
| `freshness` | `fresh`               | Only consider readings that pass the freshness gate, so stale prints cannot qualify a country. |
| `limit`     | 1 to 500, default 100 | Cap the number of returned countries.                                                          |

```bash theme={"theme":"css-variables"}
# Same screen, but only over fresh readings, limited to 20 countries
curl -s "https://api.financialdatapi.com/screener?filter=cpi_inflation_yoy:gt:3,unemployment_rate:lt:5&freshness=fresh&limit=20" \
  -H "x-api-key: $FINANCIALDATA_API_KEY"
```

<Warning>
  `freshness=fresh` is recommended for live decision-making. Without it, a country can qualify on an old print that is past its publication cadence. With it, those stale readings are excluded before filtering.
</Warning>

## More examples

<CodeGroup>
  ```bash Restrictive policy theme={"theme":"css-variables"}
  # Inflation above target and a tight policy stance
  curl -s "https://api.financialdatapi.com/screener?filter=cpi_inflation_yoy:gt:2,policy_rate:gte:4&freshness=fresh" \
    -H "x-api-key: $FINANCIALDATA_API_KEY"
  ```

  ```bash Discrete policy rates theme={"theme":"css-variables"}
  # Countries at one of a set of policy rates
  curl -s "https://api.financialdatapi.com/screener?filter=policy_rate:in:0|0.25|0.5" \
    -H "x-api-key: $FINANCIALDATA_API_KEY"
  ```

  ```bash Single-country test theme={"theme":"css-variables"}
  # Does the UK currently meet these conditions?
  curl -s "https://api.financialdatapi.com/screener?filter=cpi_inflation_yoy:gt:3&country=GBR" \
    -H "x-api-key: $FINANCIALDATA_API_KEY"
  ```
</CodeGroup>

## Errors

The screener fails closed and returns a stable error `code` on bad input.

| Situation                                          | Code          | Status |
| -------------------------------------------------- | ------------- | ------ |
| `filter` is missing                                | `bad_request` | 400    |
| A clause is malformed (not `field:operator:value`) | `bad_request` | 400    |
| An unknown operator                                | `bad_request` | 400    |
| A non-numeric value (or empty `in` set)            | `bad_request` | 400    |
| `limit` out of the 1 to 500 range                  | `bad_request` | 400    |
| An unknown query parameter                         | `bad_request` | 400    |

The error message names the offending clause so you can fix it quickly. The `@financialdatapi/client` SDK throws a `FinancialDataApiError` carrying the `code`, HTTP `status`, and `requestId`:

```typescript theme={"theme":"css-variables"}
import { FinancialDataApiError } from "@financialdatapi/client";

try {
  await client.screen("cpi_inflation_yoy:gt:notanumber");
} catch (error) {
  if (error instanceof FinancialDataApiError) {
    console.error(error.code, error.status, error.message);
  }
}
```

## Next steps

<Columns cols={2}>
  <Card title="Discover fields" icon="folder-tree" href="/apis/macro-indicators">
    The catalog guide covers the full indicator list the screener fields come from.
  </Card>

  <Card title="Pull the values" icon="chart-line" href="/apis/macro-indicators">
    Once a screen narrows your set, fetch the full point-in-time series with the observations API.
  </Card>
</Columns>
