---
title: "Execute collection"
method: POST
path: "/collections/{id}/execute"
tags: ["Collections"]
---

# Execute collection

`POST /collections/{id}/execute`

## Request

- Base URL: `https://studio.fintechstudios.com/api/v1`
- URL: `https://studio.fintechstudios.com/api/v1/collections/{id}/execute`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Execute a collection's sources and return aggregated articles. Fetches from all configured sources, applies filters, merges, and deduplicates results.

**Execute is always date-bounded, never silently result-bounded.** The window is your `date_start`/`date_end` if given, else the collection's saved date range, else **the last 30 days**. The applied window comes back as `date_start`/`date_end`/`date_window_source`, so a default window is always visible rather than inferred. Results are ordered by `published_at` descending (newest first, `url` as tiebreaker) regardless of the collection's merge strategy.

**The result cap follows the same precedence as the window:** your `max_articles`, else the collection's stored `max_articles_per_refresh`, else unbounded — every article in the window. The FTS news source is our own API, so an unbounded execute pages through it, up to a hard ceiling of 5000 articles per query. A cap caps the window rather than replacing it, and it is pushed down to the sources, so `total_in_range` is bounded by it too: only an uncapped execute reports the true size of the window. Third-party sources (web search) keep their configured per-source limit and vendor caps either way.

There is no offset/cursor pagination — every execute re-fetches live, so there is no stable result set to page over. Two signals tell you the answer was cut short: `has_more` means your cap bit, and `truncated_sources` means a source hit its own ceiling with news still unread in the window. For the latter, split the window into calendar chunks that each stay under 5000 and merge the results — see `truncated_sources`.

## Path parameters

- `id` string, required

## Request body

- object
  - `max_articles` integer: Ceiling on returned articles, applied within the window. Omit to fall back to the collection's stored max_articles_per_refresh, and if that is unset too, to every article in the window (no cap).
  - `date_start` string: Inclusive lower bound on published_at. ISO-8601 UTC datetime, or a bare YYYY-MM-DD (widened to 00:00:00.000Z that day). Defaults to the collection's saved range, else 30 days ago.
  - `date_end` string: Inclusive upper bound on published_at. Omit for 'up to now'. A bare YYYY-MM-DD is widened to 23:59:59.999Z that day.

## Response `200`

Execution result

- object
  - `data` ExecutionResult
    - `collection_id` string
    - `collection_name` string
    - `executed_at` string, date-time
    - `articles` Article[]
      - `id` string
      - `title` string
      - `summary` string
      - `source` string
      - `url` string, nullable
      - `published_at` string, date-time
      - `image_url` string, nullable: Article image URL if available
      - `sentiment` 'positive' | 'negative' | 'neutral'
    - `total_articles` integer
    - `date_start` string: Start of the window actually applied — your date_start, else the collection's saved range, else 30 days ago. Always present.
    - `date_end` string: End of the window actually applied. Always present.
    - `date_window_source` 'request' | 'collection' | 'default': Where the applied window came from. `default` means neither you nor the collection specified one and the 30-day default was used.
    - `total_in_range` integer: Articles matching the window before max_articles was applied. Bounded by what the sources returned, so it is not a corpus total.
    - `has_more` boolean: There is more news in this window than you received — because your max_articles bit, or because a source hit its own ceiling. Check truncated_sources to tell which.
    - `truncated_sources` string[]: Sources that stopped with articles still unread in this window. If a source appears here after an unbounded execute, it hit its own ceiling (FTS reaches 5000 per query) and the only way to see the rest is a narrower date range — raising max_articles will not help. Absent when nothing was truncated. To cover a window that exceeds the ceiling, split it into calendar chunks that each stay under 5000 (month by month for a busy query, week by week for a very busy one), execute each chunk, and merge the results. Chunk until no chunk reports truncation and the coverage is complete — the article order within a chunk does not matter, because every chunk that comes back untruncated is exhaustive for its own dates.
    - `source_breakdown` object: Article count per source category (news, web, other)
    - `errors` object[]
      - `source` string: Source category (news, web, other)
      - `error` string
  - `meta` Meta
    - `request_id` string
    - `credits_used` integer
    - `credits_remaining` integer
    - `rate_limit` object
      - `limit` integer
      - `remaining` integer
      - `reset` integer

## Other responses

- `400`: Validation error (no channel config)
- `403`: Access denied
- `404`: Collection not found

---

[API](https://skmtc.dev/fintechstudios/apis/fts-studio-api.md) · [All operations](https://skmtc.dev/fintechstudios/apis/fts-studio-api/llms.txt) · [OpenAPI document](https://skmtc.dev/fintechstudios/apis/fts-studio-api/revisions/4886597d1d96?raw)
