usestring-ai Feeds API

Published feeds you are subscribed to, and the data they collect.

Operations 3

GET /v1/feeds List your feeds #
GET /v1/feeds/{feed}/data List a feed's collected data #
GET /v1/feeds/{feed}/download Download one of a feed's objects #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/usestring-ai:usestring-ai-feeds-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

usestring-ai-feeds-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  description: 'The customer-facing HTTP/JSON API for Composer.


    Describe the feed you want in prose, answer the questions the builder asks, and

    approve the finished schema. Every route is under `/v1` and every route under

    `/v1` requires an API key.


    ## Authentication


    `Authorization: Bearer sk_fb_live_…`. The scheme is matched

    case-insensitively. The tenant comes from the key AND ONLY FROM THE KEY: no

    request body may carry an organization or user id, and one that does is refused

    rather than silently overwritten.


    The two documentation routes — `/openapi.yaml` and `/docs` — take no key.


    ## Errors


    Every refusal has the same body: `{"error": {"type", "code", "message", "param?", "request_id?"}}`.

    Switch on `type` to decide whether to retry, fix the request, or escalate;

    `code` is the specific reason within the family. `request_id` matches the

    `x-string-request-id` response header and our log line, so quote it when you ask

    us about a request.


    | Upstream condition | HTTP |

    | --- | --- |

    | invalid argument, out of range | 400 |

    | bad or missing key | 401 |

    | permission denied | 403 |

    | unknown build | 404 |

    | already exists, failed precondition, aborted | 409 |

    | body over 1 MiB | 413 |

    | rate limited | 429 + `Retry-After` |

    | the request was cancelled upstream | 499 |

    | not implemented upstream | 501 |

    | unavailable | 503 |

    | upstream timeout | 504 |

    | anything else | 500, with a generic message |


    The table is grpc-gateway''s canonical gRPC-to-HTTP mapping with ONE deliberate

    change: a failed precondition answers **409, not 400**. On this API that

    condition is a state conflict — input sent to a build that has finished, a gate

    answered after it stopped being pending — which you resolve by changing state,

    not by editing your request, and a 400 would send you to re-read JSON that is

    already correct.


    499 is listed for completeness and is not documented on individual operations:

    it is written when the request was already cancelled, so in practice there is

    nobody left to read it.


    **403 is never about a particular build or feed, and that is deliberate.** It

    answers only for the CALLER: an organization that is not entitled to use

    Composer (`feed_builder_disabled`), or, on `POST /v1/builds` alone, a key with no

    user recorded against it (`key_not_attributed`). A build owned by another

    organization answers a BYTE-IDENTICAL 404 to an unknown id — same code, same

    message. Nothing inspects the id, the organization, or the message to choose

    between them, because a distinguishable answer is an oracle: a caller could walk

    build ids and learn which ones are real. Do not write client code that tries to

    tell the two apart.


    ## Scope: this API serves supervised builds only


    Every build starts supervised: it may stop and ask you things, and it publishes

    by way of a publish-review gate you answer. There is no unattended mode and no

    way to pre-authorize publishing, so `POST /v1/builds` takes no lever for

    either — the body is the query and nothing else.


    ## Streaming


    `GET /v1/builds/{id}/events` is server-sent events. OpenAPI cannot describe a

    stream of framed events, so **generated clients will not handle that endpoint** —

    use an SSE client for it and a generated client for the other ten operations.

    The frame shape and event names are on the operation itself.'
  title: Composer Feeds API
  version: v1
servers:
- description: The origin this document was served from.
  url: /
security:
- bearerAuth: []
tags:
- description: Published feeds you are subscribed to, and the data they collect.
  name: Feeds
paths:
  /v1/feeds:
    get:
      description: 'The published feeds your organization currently holds an active subscription to, by name. The name is the handle every other feed route takes.


        A feed whose subscription has lapsed is not listed, and is indistinguishable from one that does not exist — see the 404 on the data operation.


        `data_available: false` means the feed is not delivered to object storage at all (it goes to Snowflake, or to your own bucket), so its data operation will refuse rather than return an empty list. That is NOT the same as "no data collected yet".'
      operationId: listFeeds
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedList'
          description: Your feeds.
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: The API key is missing, malformed, unknown, or its secret does not match. All four collapse to ONE code and message so the key format cannot be probed one refusal at a time.
          headers:
            WWW-Authenticate:
              description: The authentication scheme. Names the scheme only — never why the presented key failed.
              schema:
                type: string
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: 'Your ORGANIZATION is not entitled to use Composer. Code `feed_builder_disabled`.


            The key itself is genuine — do not rotate it. It starts working again the moment the entitlement is restored, and until then your organization''s browser sessions are refused for the same reason. Every operation on this API answers this identically, so it tells you nothing about whether any particular build or feed exists.'
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Rate limited by the core.
          headers:
            Retry-After:
              description: Seconds to wait. Always present and never zero; the core's own backoff when it supplies one, otherwise a one-second floor.
              schema:
                type: string
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: 'An unmapped failure. The message is generic on purpose: the underlying text may name our infrastructure.'
        '501':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: This deployment has no feed catalog. Code `feed_data_unavailable`. Ours to fix.
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: 'OUR outage, not your key: the key store or the core is unreachable. Retriable. Note that an unreachable key store answers 503 rather than 401 precisely so you do not rotate a working key during our incident.'
      summary: List your feeds
      tags:
      - Feeds
  /v1/feeds/{feed}/data:
    get:
      description: 'What the feed has collected, each object with a signed URL you can fetch directly. This is the ongoing run data — the same artifacts the delivery subscription mails links to, reached by a program instead of by a person. `GET /v1/builds/{id}/data` is a different thing: the one-shot dataset a *build* harvested before the feed started running.


        LINKS ARE SHORT-LIVED AND MINTED PER REQUEST. `expires_in` defaults to 3600 seconds, cannot be less than 60, and cannot exceed 604800 (7 days) — an out-of-range value is a 400, never silently clamped, because a pipeline built on a link it believes lasts 30 days fails a week later for no reason its author changed. `url` is a bearer link: anyone holding it can fetch that object until `expires_at`. Fetch and discard rather than storing them.


        `path` is relative to the feed''s own subtree and is stable across calls, so it is what you compare to tell an object you already have from a new one. It does not name a bucket.


        PAGING. `page_size` defaults to 100 and caps at 1000. When `truncated` is true, pass the returned `next_page_token` as `page_token` to continue from the first object not included in this page. The token follows the object store''s lexicographic order; it does not freeze a snapshot while new objects are collected. `page_size` also bounds raw objects examined, so metadata filtering in a shared prefix can produce a short or empty page with `truncated` still true. Continue until it is false.


        FILTERING BY TIME. `updated_after` (inclusive) and `updated_before` (exclusive) keep only objects whose `updated_at` falls in that window. The filter applies to each page after it is listed, because the object store lists by name and not by time, so it narrows what is returned without reducing how many pages there are. Expect short or empty pages with `truncated` still true, and keep paging until it is false.


        DOWNLOADING THROUGH THIS HOST. If your network cannot reach the object store a `url` names, pass the object''s `path` to `GET /v1/feeds/{feed}/download` instead, which streams the same bytes from this host.


        DELIVERY FORMATS. A subscription can carry a delivery format: renamed and dropped columns, a row window, a file-naming template, and an encoding and container such as a zip of one JSON array. For such a subscription this operation lists the format''s files instead of the raw export, one entry per file each run yields (on a quarter-end run, a full-history file as well). Their `path` is `/`, their `url` is this API''s own download path for that file (fetch it with the same key; it does not expire, so there is no `expires_at`), and `size` is absent because the file is built when it is downloaded. `expires_in` has no effect on them.'
      operationId: getFeedData
      parameters:
      - description: The feed name, as returned by GET /v1/feeds. Feeds are addressed by name and never by a numeric id.
        in: path
        name: feed
        required: true
        schema:
          type: string
      - description: Link lifetime in seconds. Default 3600, minimum 60, maximum 604800. Out-of-range values are refused with 400.
        in: query
        name: expires_in
        schema:
          default: 3600
          maximum: 604800
          minimum: 60
          type: integer
      - description: Maximum objects to examine and return. Default 100, maximum 1000. Shared-prefix filtering can return fewer.
        in: query
        name: page_size
        schema:
          default: 100
          maximum: 1000
          minimum: 1
          type: integer
      - description: The `next_page_token` from the previous page. Absent means the first page.
        in: query
        name: page_token
        schema:
          type: string
      - description: Keep only objects with `updated_at` at or after this RFC 3339 timestamp.
        in: query
        name: updated_after
        schema:
          format: date-time
          type: string
      - description: Keep only objects with `updated_at` strictly before this RFC 3339 timestamp. Must be later than `updated_after` when both are given.
        in: query
        name: updated_before
        schema:
          format: date-time
          type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataList'
          description: The feed's objects, with a link for each.
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: The request is malformed. `param` names the offending field where there is one. Note that a body carrying an organization or user id (`org_id`, `organization_id`, `user_id`, `submitted_by` and their casing variants, at any nesting depth) is refused here rather than silently overwritten — the tenant comes from the API key and only from the API key. `expires_in` and `page_size` must be within their documented bounds; `page_token` must be a token returned by this operation; `updated_after` and `updated_before` must be RFC 3339 timestamps, the second later than the first.
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: The API key is missing, malformed, unknown, or its secret does not match. All four collapse to ONE code and message so the key format cannot be probed one refusal at a time.
          headers:
            WWW-Authenticate:
              description: The authentication scheme. Names the scheme only — never why the presented key failed.
              schema:
                type: string
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: 'Your ORGANIZATION is not entitled to use Composer. Code `feed_builder_disabled`.


            The key itself is genuine — do not rotate it. It starts working again the moment the entitlement is restored, and until then your organization''s browser sessions are refused for the same reason. Every operation on this API answers this identically, so it tells you nothing about whether any particular build or feed exists.'
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: No such feed. A feed your organization is NOT subscribed to, and one whose subscription has lapsed, both answer this identically — there is no 403, because a distinguishable answer would let a caller walk feed names and learn which of them are real. Do not write client code that tries to tell the cases apart.
        '409':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: This feed is not delivered to object storage, so it has no object listing and never will. Code `feed_data_not_delivered`. Distinct from an empty list, which means "nothing collected yet".
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Rate limited by the core.
          headers:
            Retry-After:
              description: Seconds to wait. Always present and never zero; the core's own backoff when it supplies one, otherwise a one-second floor.
              schema:
                type: string
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: 'An unmapped failure. The message is generic on purpose: the underlying text may name our infrastructure.'
        '501':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: This deployment has no feed catalog or object store. Code `feed_data_unavailable`. Ours to fix.
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: 'OUR outage, not your key: the key store or the core is unreachable. Retriable. Note that an unreachable key store answers 503 rather than 401 precisely so you do not rotate a working key during our incident.'
      summary: List a feed's collected data
      tags:
      - Feeds
  /v1/feeds/{feed}/download:
    get:
      description: 'Streams one collected object from this host. It is the same object the data listing''s `url` points at, for callers whose network allows this API''s host but not the object store''s.


        `path` is the object''s `path` exactly as the data listing returns it. It is resolved inside the feed''s own data, so it can only name an object the listing would show you.


        The response is the object''s bytes with its stored `Content-Type` (`application/octet-stream` when none is recorded), `Content-Length`, and `Content-Disposition: attachment`. Headers are sent before the body, so a failure part-way through cannot change the status: it shows up as a body shorter than `Content-Length`, which HTTP clients report as an error. Retry the download when that happens.


        Each pod streams a bounded number of downloads at once, and each organization gets a share of them (8 per pod). Over either, the answer is 429 with `Retry-After`, so fetch a backlog a few files at a time. A download whose caller stops reading for 30 seconds is dropped.


        For a subscription with a delivery format, the file is built from its run when you ask for it, with the format''s own `Content-Type` (for example `application/zip`). The same file downloaded twice is byte-identical.'
      operationId: downloadFeedObject
      parameters:
      - description: The feed name, as returned by GET /v1/feeds. Feeds are addressed by name and never by a numeric id.
        in: path
        name: feed
        required: true
        schema:
          type: string
      - description: The object's `path`, as returned by GET /v1/feeds/{feed}/data.
        in: query
        name: path
        required: true
        schema:
          maxLength: 1024
          type: string
      responses:
        '200':
          content:
            application/octet-stream: {}
          description: The object's bytes.
          headers:
            Content-Disposition:
              description: '`attachment; filename="<last segment of path>"`.'
              schema:
                type: string
            Last-Modified:
              description: When the object was last written.
              schema:
                type: string
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: The request is malformed. `param` names the offending field where there is one. Note that a body carrying an organization or user id (`org_id`, `organization_id`, `user_id`, `submitted_by` and their casing variants, at any nesting depth) is refused here rather than silently overwritten — the tenant comes from the API key and only from the API key. `path` is required, at most 1024 characters, and must name a file rather than end in `/`.
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: The API key is missing, malformed, unknown, or its secret does not match. All four collapse to ONE code and message so the key format cannot be probed one refusal at a time.
          headers:
            WWW-Authenticate:
              description: The authentication scheme. Names the scheme only — never why the presented key failed.
              schema:
                type: string
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: 'Your ORGANIZATION is not entitled to use Composer. Code `feed_builder_disabled`.


            The key itself is genuine — do not rotate it. It starts working again the moment the entitlement is restored, and until then your organization''s browser sessions are refused for the same reason. Every operation on this API answers this identically, so it tells you nothing about whether any particular build or feed exists.'
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: No such feed, or no such object in it. Code `not_found` for the feed, answered exactly as on the data operation, and `object_not_found` for a path that names nothing in a feed you hold.
        '409':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: This feed is not delivered to object storage (code `feed_data_not_delivered`); or, for a delivery format, the run is too large to format on request (`run_too_large`) or a full-history file was asked of a run that did not collect the full history (`history_incomplete`).
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Too many downloads in progress on this pod. Code `too_many_downloads`.
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: string
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: 'An unmapped failure. The message is generic on purpose: the underlying text may name our infrastructure.'
        '501':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: This deployment has no feed catalog or object store. Code `feed_data_unavailable`. Ours to fix.
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: 'OUR outage, not your key: the key store or the core is unreachable. Retriable. Note that an unreachable key store answers 503 rather than 401 precisely so you do not rotate a working key during our incident.'
      summary: Download one of a feed's objects
      tags:
      - Feeds
components:
  schemas:
    ErrorResponse:
      description: The one error envelope every refusal on this API uses.
      properties:
        error:
          $ref: '#/components/schemas/ErrorDetail'
      required:
      - error
      type: object
    FeedList:
      properties:
        data:
          items:
            $ref: '#/components/schemas/FeedSummary'
          type: array
        object:
          type: string
      required:
      - object
      - data
      type: object
    DataObject:
      properties:
        expires_at:
          format: date-time
          type: string
        object:
          type: string
        path:
          type: string
        size:
          format: int64
          type: integer
        updated_at:
          format: date-time
          type: string
        url:
          type: string
      required:
      - path
      - object
      - updated_at
      - url
      type: object
    DataList:
      properties:
        data:
          items:
            $ref: '#/components/schemas/DataObject'
          type: array
        feed:
          type: string
        next_page_token:
          type: string
        object:
          type: string
        truncated:
          type: boolean
      required:
      - object
      - feed
      - data
      - truncated
      type: object
    ErrorDetail:
      description: The reason a request was refused. `type` is the family a client switches on; `code` is the specific reason within it; `param` names the offending request field when there is one and is omitted rather than sent empty.
      properties:
        code:
          type: string
        message:
          type: string
        param:
          type: string
        request_id:
          type: string
        type:
          type: string
      required:
      - type
      - code
      - message
      type: object
    FeedSummary:
      properties:
        data_available:
          type: boolean
        name:
          type: string
        object:
          type: string
      required:
      - name
      - object
      - data_available
      type: object
  securitySchemes:
    bearerAuth:
      description: 'A Composer API key: `Authorization: Bearer sk_fb_live_…`.'
      scheme: bearer
      type: http