---
url: https://docs.dataflair.ai/marketplace/ad-server-api/operations/forecast.md
description: >-
  How many impressions a slot can deliver over a flight. DataFlair shows it to
  an advertiser before they book.
---

# POST /forecast: Availability and forecast

When an advertiser assembles a booking, DataFlair shows how many impressions each slot can realistically deliver over the chosen flight. The advertiser then books a sensible amount and does not over-commit or under-commit. DataFlair does this with `ForecastService.getAvailabilityForecast` on Google Ad Manager. This endpoint is your equivalent.

The call is read-only and creates nothing. It asks "what if I booked this?"

## What we send

::: code-group

```http [HTTP]
POST /forecast HTTP/1.1
Host: ads.example.com
Authorization: Bearer sk_live_9f2c…
Content-Type: application/json
```

```bash [curl]
curl -X POST "$BASE_URL/forecast" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json
```

:::

The body, saved as `request.json`:

::: code-group

```json \[exemplary/forecast/request.json]
{
  "inventory_id": "slot_728x90_home",
  "flight": {
    "start_date": "2026-08-01",
    "end_date": "2026-08-31"
  },
  "targeting": {
    "geo": [
      "DE",
      "AT"
    ]
  },
  "sizes": [
    "728x90"
  ]
}

```

:::

The JSON on this page is read from the [fixture files](/marketplace/ad-server-api/fixtures), so it cannot drift from the test suite.

| Field | Type | Required | Meaning |
| --- | --- | --- | --- |
| `inventory_id` | string | yes | The slot being forecast. See [Inventory identity](/marketplace/ad-server-api/inventory). |
| `flight.start_date`, `flight.end_date` | string | yes | Inclusive date range, `YYYY-MM-DD`, read in your account timezone from `/health`. |
| `targeting.geo` | array of strings | no | ISO-3166-1 alpha-2 country codes. Absent or empty means no geo restriction (worldwide). |
| `sizes` | array of strings | no | Creative sizes under consideration. They improve accuracy. Google Ad Manager's own forecast is more precise when the creative placeholder size is supplied. |

## What you return

Status `200` with a JSON body:

::: code-group

```json \[exemplary/forecast/expected-response.json]
{
  "inventory_id": "slot_728x90_home",
  "available_impressions": 1420000,
  "forecasted_impressions": 2100000,
  "unit_type": "IMPRESSIONS"
}

```

:::

| Field | Type | Required | Meaning |
| --- | --- | --- | --- |
| `inventory_id` | string | yes | Echoes the slot, so DataFlair can match the response to the request. |
| `available_impressions` | integer | yes | Impressions **still reservable** for this slot over the flight, after existing commitments. An advertiser can book against this number. |
| `forecasted_impressions` | integer | yes | Total impressions the slot is **predicted to deliver** over the flight, before existing commitments. It is always greater than or equal to `available_impressions`. |
| `unit_type` | string | no | Defaults to `IMPRESSIONS`. It is there so the contract can extend to other units later. Only `IMPRESSIONS` is used today. |

### How this maps to Google Ad Manager

GAM's forecast returns four counts: `availableUnits`, `matchedUnits`, `possibleUnits` and `reservedUnits`. DataFlair needs two of them, and this endpoint asks for those two directly.

* `available_impressions` is GAM `availableUnits`: what is left to sell.
* `forecasted_impressions` is GAM `matchedUnits`: the total the targeting predicts.

You do not need GAM's other counts. If your platform models availability differently, map your closest concepts onto these two and describe the mapping in your handoff notes.

## If you cannot forecast

Pick one of two options. Do not invent a number.

1. **Preferred.** Declare `"forecast": false` in [`GET /health`](/marketplace/ad-server-api/operations/health). DataFlair then does not call `POST /forecast` and shows the availability the publisher listed. The booking still works. It is not checked against a live prediction. DataFlair takes the same position with Revive today.
2. If `forecast` is `true` but one slot cannot be forecast, return `501 Not Implemented` with `{ "code": "forecast_unsupported", "message": "…" }`. DataFlair treats that slot as "no forecast available" and degrades gracefully. It does not block the booking.

## Errors

| Status | What caused it | What DataFlair does | What to do |
| --- | --- | --- | --- |
| `400` | Malformed request. | Treats it as a bug, logs it and shows it. | Check the body against the fields above. |
| `401` | Missing, invalid or expired key. | Marks the connection as `error`. | Check the key. |
| `403` | The key lacks a required scope. | Marks the connection as `error`. | Widen the key's scope. |
| `404` | Unknown `inventory_id`. | Skips the item and reports it. | Return `404` only for a slot that does not exist. |
| `422` | Valid shape, invalid values, such as a bad date range or an incompatible size. | Shows your `message`, so the operator can fix the booking. | Return a clear `message`. |
| `429` | Rate limit exceeded. | Backs off and retries per `Retry-After`. | Send `Retry-After` in seconds. |
| `501` | This slot cannot be forecast. | Degrades gracefully. | See "If you cannot forecast". |
| `5xx` | Server error. | Retries with backoff. | Check your server's logs. |

Every error body has the shape described in [Conventions](/marketplace/ad-server-api/conventions#error-model).

## Accuracy and performance

* **Caching is welcome.** DataFlair caches forecast results briefly on its side. Its GAM path caches per slot, geo and month for a few minutes. A forecast that is a few minutes old is fine. A slow forecast that blocks the booking screen is not. Aim to answer in well under a second.
* **Rounding is fine.** DataFlair rounds forecast numbers before it shows them to advertisers. You do not need an exact-to-the-impression figure.
* **Batching is optional.** DataFlair may forecast several slots while an advertiser browses. A single-slot endpoint is enough, because DataFlair runs the calls in parallel. If you can offer a batch variant that accepts an array of `inventory_id` values, tell DataFlair, and it can use it to cut round trips.

## Timeouts and retries

DataFlair does not call this endpoint yet. The platform's configured timeouts for calls to your server are 10 seconds to connect and 30 seconds in total.

## Verify

Run the [conformance check](/marketplace/ad-server-api/conformance) with `--only forecast` to check this endpoint against the [fixtures](/marketplace/ad-server-api/fixtures).
