# Raw flow feed for a ticker (Flow Score + FlowBonus per trade)

`GET https://api.skylit.ai/v1/flow/{ticker}`

API: Flowseeker. Credits: 1.

Returns the most recent options trades for `{ticker}` within the
requested timeframe, each scored on Skylit's directional Flow Score
(-100 → +100) and conviction-weighted FlowBonus. The response also
includes timeframe-level VWF / SDF / FIR aggregates.

**Paging.** Trades come newest first. When a page is full (`limit`
rows read), `meta.nextCursor` is set: pass it back as `cursor` with the
same filters to get the next, older page. It resumes exactly after the
last row, so no trade is skipped or repeated, even when several share
one nanosecond timestamp. No `nextCursor` means you have reached the
start of the window; a `cursor` page with no trades returns `200` with
an empty `trades` array. `min_flow_score`, `min_flow_bonus` and
`min_rvol` filter after the read, so a page can hold fewer than
`limit` trades while `nextCursor` is still set: keep following it.
`meta.nextEndTime` is the last row's exact time (nanoseconds) for
clients that page with `end_time`; it is inclusive, so trades at that
instant can repeat. Prefer `cursor`.

**Backfilling a day** for one contract, 500 trades per request:

    GET /v1/flow/SPY?date=2026-09-30&expiration=2026-09-30&min_strike=660&max_strike=660&option_type=call&limit=500
    → meta.nextCursor = "dDEuMTc5..."
    GET /v1/flow/SPY?date=2026-09-30&expiration=2026-09-30&min_strike=660&max_strike=660&option_type=call&limit=500&cursor=dDEuMTc5...
    → repeat until meta.nextCursor is absent

Each page costs one request. Splitting the window around the 500-row
limit instead (bisecting) re-reads overlapping ranges, spends the
account's query budget several times over, and gets an
`X-Skylit-Hint` response header pointing here.

## Authentication

Send your Skylit API key as a bearer token: `Authorization: Bearer <key>`. No other header is accepted.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `ticker` | path | string | yes | Underlying ticker symbol (uppercase, e.g. `SPY`, `AAPL`). |
| `timeframe` | query | string | no | Trailing window label for the request. Supported values: `5m`, `15m`, `1h`, `4h`, `1d`. (one of `5m`, `15m`, `1h`, `4h`, `1d`; default `1h`) |
| `limit` | query | integer | no | Max trades returned. Server caps this at 500. (default `100`; min 1; max 500) |
| `cursor` | query | string | no | `meta.nextCursor` from the previous page, passed back unchanged with the same filters. Opaque; an invalid value returns `400`. |
| `min_premium` | query | number (double) | no | Minimum total premium per trade (USD). |
| `option_type` | query | string | no | Filter to calls or puts. `all` returns both. (one of `call`, `put`, `all`; default `all`) |
| `trade_type` | query | string | no | Filter by trade type. Comma-separated for multiple; every token must be one of the listed values. (one of `sweep`, `multi_leg`, `all`; default `all`) |
| `moneyness` | query | string | no | Moneyness category filter. Comma-separated for multiple (e.g. `otm,deep_otm`). Every token must be one of the listed values; an unknown token returns `400`. (one of `deep_itm`, `itm`, `atm`, `otm`, `deep_otm`, `all`; default `all`) |
| `start_time` | query | string | no | Optional lower bound for the trade window. Accepts RFC 3339 (`2026-05-27T13:30:00Z`) or Unix seconds. Omit to use the timeframe. |
| `end_time` | query | string | no | Optional upper bound (RFC 3339 or Unix seconds). |
| `max_premium` | query | number (double) | no | Maximum total premium per trade (USD). |
| `min_contracts` | query | integer | no | Minimum contract size per trade. (min 0) |
| `max_contracts` | query | integer | no | Maximum contract size per trade. (min 0) |
| `single_leg_only` | query | boolean | no | If `true`, exclude trades flagged as part of a multi-leg structure. (default `false`) |
| `min_dte` | query | integer | no | Minimum days to expiration. |
| `max_dte` | query | integer | no | Maximum days to expiration. |
| `min_strike` | query | number (double) | no | Minimum strike price (inclusive). |
| `max_strike` | query | number (double) | no | Maximum strike price (inclusive). |
| `expiration` | query | string (date) | no | Filter to a single expiration date (YYYY-MM-DD). |
| `conviction_weights` | query | string | no | Optional JSON object overriding the Flow Score conviction weights. Weights must be non-negative and sum to within 0.95–1.05, else 400. |
| `min_flow_score` | query | integer | no | Filter to trades with `flowScore` ≥ this value (-100..100). (min -100; max 100) |
| `min_flow_bonus` | query | integer | no | Filter to trades with `flowBonus` ≥ this value. (min 0) |
| `min_rvol` | query | number (double) | no | Filter to trades with relative volume ≥ this multiple. (min 0) |
| `include_clusters` | query | boolean | no | If `true`, attach `cluster*` fields when a trade is part of a multi-leg cluster (sweep, condor, etc.). (default `true`) |
| `date` | query | string (date) | no | Trading date (YYYY-MM-DD). Defaults to current trading date. |

## Example request

```bash
curl "https://api.skylit.ai/v1/flow/SPY" \
  -H "Authorization: Bearer $SKYLIT_API_KEY"
```

## Responses

### 200

Flow feed for `{ticker}`.

Headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, `X-Credits-Remaining`, `X-Skylit-Hint`.

Shape (placeholder values):

```json
{
  "data": {
    "ticker": "string",
    "timeframe": "string",
    "trades": [
      {
        "timestamp": "string",
        "tradeId": "flow_188afe42c3a77af2_0",
        "optionType": "CALL",
        "strike": 0,
        "expiration": "string",
        "dte": 0,
        "dteCategory": "0DTE",
        "dteFactor": 0,
        "dteMultiplier": 0,
        "contracts": 0,
        "premium": 0,
        "price": 0,
        "bid": 0,
        "ask": 0,
        "mid": 0,
        "spreadWidth": 0,
        "spreadWidthPct": 0,
        "liquidityGrade": "HIGH",
        "underlyingPrice": 0,
        "isSweep": false,
        "isMultiLeg": false,
        "isCross": false,
        "exchangeCount": 0,
        "moneyness": "DEEP_ITM",
        "moneynessPct": 0,
        "moneynessWeight": 0,
        "combinedMoneynessDteWeight": 0,
        "delta": 0,
        "notionalDeltaExposure": 0,
        "openInterest": 0,
        "dailyVolume": 0,
        "volOiRatio": 0,
        "volOiScore": 0,
        "sizeOiRatio": 0,
        "sizeOiScore": 0,
        "oiIsZero": false,
        "rvol": 0,
        "rvolScore": 0,
        "rvolCategory": "string",
        "iv": 0,
        "ivChangePct": 0,
        "relativePremium": 0,
        "scores": {
          "flowScore": 0,
          "flowScoreInterpretation": "strong_bullish",
          "flowBonus": 0,
          "flowBonusInterpretation": "high_conviction",
          "baseDirection": 0,
          "convictionMultiplier": 0
        },
        "cluster": {
          "clusterId": "string",
          "clusterTradeCount": 0,
          "clusterTotalPremium": 0,
          "clusterTimeSpanSeconds": 0
        }
      }
    ],
    "aggregate": {
      "vwf": 0,
      "sdf": 0,
      "fir": 0
    },
    "tradeCount": 0,
    "sweepCount": 0,
    "totalPremium": 0,
    "queryTimeMs": 0
  },
  "meta": {
    "timestamp": "string",
    "requestId": "d7574836",
    "nextCursor": "string",
    "nextEndTime": "string"
  }
}
```

### 400

Request validation failed.

invalidParam:

```json
{
  "error": {
    "code": "INVALID_PARAMETER",
    "message": "Invalid value 'bogus' for 'timeframe'. Allowed: 5m, 15m, 1h, 4h, 1d."
  }
}
```

### 401

Missing or invalid API key.

missingKey:

```json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Authentication required"
  }
}
```

### 402

The account's shared Skylit credit balance is lower than this route's cost. Top up to continue. Carries `X-Credits-Remaining: 0`.

Headers: `X-Credits-Remaining`.

outOfCredits:

```json
{
  "error": {
    "code": "insufficient_credits",
    "message": "Out of credits. Top up to continue making requests."
  }
}
```

### 403

Unknown, revoked or expired API key (the gateway's `forbidden`), the account's API access is suspended (`account_suspended`) or blocked (`account_blocked`). Not retryable.

badKey:

```json
{
  "error": {
    "code": "FORBIDDEN",
    "message": "Access to this API has been disallowed"
  }
}
```

accountSuspended:

```json
{
  "error": {
    "code": "account_suspended",
    "message": "API access has been suspended for this account. Contact support."
  }
}
```

### 404

Unknown ticker or contract (`SYMBOL_NOT_FOUND`), or no data for the requested window. Not charged.

unknownSymbol:

```json
{
  "error": {
    "code": "SYMBOL_NOT_FOUND",
    "message": "Unknown ticker 'ZZZZQ'."
  }
}
```

noData:

```json
{
  "error": {
    "code": "NOT_FOUND",
    "message": "No trades found for AAPL on 2026-05-27 with timeframe 1d"
  }
}
```

### 429

Either the key exceeded its requests-per-minute limit (`X-RateLimit-Limit`; no `Retry-After`, wait until `X-RateLimit-Reset`), or the account is over one of the API's own limits, such as the query budget (queries running at once, fresh queries per second): `TOO_MANY_CONCURRENT_QUERIES` or `RATE_LIMITED` with `Retry-After`. Not charged; retry after the wait.

tooFast:

```json
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded."
  }
}
```

queryBudget:

```json
{
  "error": {
    "code": "TOO_MANY_CONCURRENT_QUERIES",
    "message": "This account already has 16 queries running on the shared query engine across all servers (limit ch.concurrent.customer). Cached answers are not affected. Retry after Retry-After; to backfill a day, follow meta.nextCursor instead of splitting time windows. See https://www.skylit.ai/docs/api-reference/rate-limits-and-retries",
    "retry_after": 1,
    "docs_url": "https://www.skylit.ai/docs/api-reference/errors#too_many_concurrent_queries"
  }
}
```

### 500

Unexpected server error (`INTERNAL_ERROR`, `DATABASE_ERROR`). Not charged; safe to retry with exponential backoff.

internal:

```json
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Internal server error"
  }
}
```

### 503

Underlying data source temporarily unavailable, the credit balance could not be verified (`credit_check_failed`), or the API is paused for maintenance (`api_paused`, with a `Retry-After` header and a `retry_after` field in seconds). Not charged; safe to retry.

ingestionLag:

```json
{
  "error": {
    "code": "UNAVAILABLE",
    "message": "Live feed is degraded; please retry in a few seconds."
  }
}
```

creditCheckFailed:

```json
{
  "error": {
    "code": "credit_check_failed",
    "message": "Could not verify credit balance. Please retry."
  }
}
```

### 504

The request did not complete within 25 seconds. Not charged; narrow the window or retry.

tooSlow:

```json
{
  "error": {
    "code": "GATEWAY_TIMEOUT",
    "message": "The request took too long and was not completed. It was not charged; narrow the request or retry."
  }
}
```
