openapi: 3.0.3 info: title: InfoBeam Feed Gateway API version: "1.0.0" description: >- Convert Telegram channels, subreddits, GitHub repos, Mastodon/Bluesky profiles, YouTube channels, RSS/Atom feeds and radio streams into a clean RSS, Atom or JSON Feed with one authenticated API call. See https://infobeam.net/gateway/ for pricing and https://infobeam.net/gateway/docs/ for the interactive playground. contact: url: https://infobeam.net/gateway/ license: name: See infobeam.net for terms servers: - url: https://infobeam.net security: - bearerAuth: [] tags: - name: feeds description: Source-to-feed conversion endpoints - name: keys description: Self-serve API key management (not bearer-gated — scoped to a browser cookie instead) paths: /gateway/v1/telegram/{channel}: get: operationId: telegramFeed tags: [feeds] summary: Public Telegram channel as a feed parameters: - $ref: '#/components/parameters/channel' - $ref: '#/components/parameters/format' - $ref: '#/components/parameters/before' responses: '200': { $ref: '#/components/responses/Feed' } '304': { $ref: '#/components/responses/NotModified' } '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '404': { $ref: '#/components/responses/NotFound' } '429': { $ref: '#/components/responses/TooManyRequests' } '451': { $ref: '#/components/responses/LegalRestriction' } '502': { $ref: '#/components/responses/BadGateway' } /gateway/v1/reddit/{subreddit}: get: operationId: redditFeed tags: [feeds] summary: Subreddit as a feed parameters: - $ref: '#/components/parameters/subreddit' - $ref: '#/components/parameters/format' responses: '200': { $ref: '#/components/responses/Feed' } '304': { $ref: '#/components/responses/NotModified' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '404': { $ref: '#/components/responses/NotFound' } '429': { $ref: '#/components/responses/TooManyRequests' } '502': { $ref: '#/components/responses/BadGateway' } /gateway/v1/github/{owner}/{repo}: get: operationId: githubFeed tags: [feeds] summary: GitHub repository releases/tags/commits as a feed parameters: - $ref: '#/components/parameters/owner' - $ref: '#/components/parameters/repo' - name: type in: query description: 'Feed type. Defaults to "releases".' schema: { type: string, enum: [releases, tags, commits], default: releases } - name: branch in: query description: Branch name, only used when type=commits (defaults to the repo's default branch). schema: { type: string } - $ref: '#/components/parameters/format' responses: '200': { $ref: '#/components/responses/Feed' } '304': { $ref: '#/components/responses/NotModified' } '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '404': { $ref: '#/components/responses/NotFound' } '429': { $ref: '#/components/responses/TooManyRequests' } '502': { $ref: '#/components/responses/BadGateway' } /gateway/v1/mastodon: get: operationId: mastodonFeed tags: [feeds] summary: Mastodon public account or hashtag as a feed description: Exactly one of `handle` or `tag` is required. parameters: - name: handle in: query description: 'Public account handle, e.g. user@instance.social.' schema: { type: string } - name: tag in: query description: Hashtag (without the leading #). schema: { type: string } - name: instance in: query description: 'Instance to search the hashtag on. Only used with tag. Defaults to mastodon.social.' schema: { type: string } - $ref: '#/components/parameters/format' responses: '200': { $ref: '#/components/responses/Feed' } '304': { $ref: '#/components/responses/NotModified' } '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '404': { $ref: '#/components/responses/NotFound' } '429': { $ref: '#/components/responses/TooManyRequests' } '502': { $ref: '#/components/responses/BadGateway' } /gateway/v1/bluesky: get: operationId: blueskyFeed tags: [feeds] summary: Bluesky public profile as a feed parameters: - name: handle in: query required: true description: 'Profile handle (e.g. user.bsky.social) or a did:plc:… identifier.' schema: { type: string } - $ref: '#/components/parameters/format' responses: '200': { $ref: '#/components/responses/Feed' } '304': { $ref: '#/components/responses/NotModified' } '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '403': { $ref: '#/components/responses/Forbidden' } '404': { $ref: '#/components/responses/NotFound' } '429': { $ref: '#/components/responses/TooManyRequests' } '502': { $ref: '#/components/responses/BadGateway' } /gateway/v1/youtube: get: operationId: youtubeFeed tags: [feeds] summary: YouTube channel uploads as a feed parameters: - name: url in: query required: true description: A YouTube @handle, channel URL, or UC… channel ID. schema: { type: string } - $ref: '#/components/parameters/format' responses: '200': { $ref: '#/components/responses/Feed' } '304': { $ref: '#/components/responses/NotModified' } '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '429': { $ref: '#/components/responses/TooManyRequests' } '502': { $ref: '#/components/responses/BadGateway' } /gateway/v1/rss: get: operationId: rssFeed tags: [feeds] summary: Any RSS/Atom feed URL, re-served in the requested format parameters: - name: url in: query required: true description: The source feed's URL. schema: { type: string, format: uri } - $ref: '#/components/parameters/format' - $ref: '#/components/parameters/before' responses: '200': { $ref: '#/components/responses/Feed' } '304': { $ref: '#/components/responses/NotModified' } '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '429': { $ref: '#/components/responses/TooManyRequests' } '502': { $ref: '#/components/responses/BadGateway' } /gateway/v1/radio: get: operationId: radioFeed tags: [feeds] summary: A stream or playlist URL as a single-item "Live" feed parameters: - name: url in: query required: true description: A direct stream URL or a .pls/.m3u/.m3u8 playlist URL. schema: { type: string, format: uri } - $ref: '#/components/parameters/format' responses: '200': { $ref: '#/components/responses/Feed' } '304': { $ref: '#/components/responses/NotModified' } '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '429': { $ref: '#/components/responses/TooManyRequests' } '451': { $ref: '#/components/responses/LegalRestriction' } '502': { $ref: '#/components/responses/BadGateway' } /api/keys: post: operationId: createAPIKey tags: [keys] summary: Mint a new API key (Free tier, active immediately) security: [] responses: '200': description: Key created. The raw key is only ever returned here — store it now. content: application/json: schema: { $ref: '#/components/schemas/NewAPIKey' } '429': { $ref: '#/components/responses/TooManyRequests' } get: operationId: listAPIKeys tags: [keys] summary: List your own API keys (scoped to the browser's ib_uid cookie) security: [] responses: '200': description: Your keys, without raw key values. content: application/json: schema: type: object properties: data: type: array items: { $ref: '#/components/schemas/APIKey' } /api/keys/{id}: delete: operationId: revokeAPIKey tags: [keys] summary: Revoke a key you own security: [] parameters: - name: id in: path required: true schema: { type: string } responses: '204': { description: Revoked. } '404': { description: Unknown key, or a key owned by someone else. } /api/keys/{id}/usage: get: operationId: getAPIKeyUsage tags: [keys] summary: Current-month request count and quota for a key you own security: [] parameters: - name: id in: path required: true schema: { type: string } responses: '200': description: Usage for the current calendar month (UTC). content: application/json: schema: { $ref: '#/components/schemas/APIKeyUsage' } '404': { description: Unknown key, or a key owned by someone else. } components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: 'ibk_...' description: 'Pass your key as `Authorization: Bearer ibk_...`. Get one free at https://infobeam.net/gateway/.' parameters: channel: name: channel in: path required: true description: Telegram channel username, without the @. schema: { type: string } subreddit: name: subreddit in: path required: true schema: { type: string } owner: name: owner in: path required: true description: GitHub repository owner. schema: { type: string } repo: name: repo in: path required: true description: GitHub repository name. schema: { type: string } format: name: format in: query description: >- Output format. Also honored via the Accept header when omitted. Defaults to rss. schema: { type: string, enum: [rss, atom, jsonfeed], default: rss } before: name: before in: query description: Opaque pagination cursor from a previous response's nextCursor, for feeds that support it (Telegram, generic RSS/Atom). schema: { type: string } responses: Feed: description: The converted feed, in the negotiated format. headers: ETag: { schema: { type: string }, description: Strong ETag derived from the response body. } Last-Modified: { schema: { type: string } } Cache-Control: { schema: { type: string } } X-RateLimit-Limit: { schema: { type: integer }, description: Per-minute burst limit for this key. } X-RateLimit-Remaining: { schema: { type: integer } } X-RateLimit-Reset: { schema: { type: integer }, description: Unix timestamp when the burst window resets. } X-Quota-Limit: { schema: { type: integer }, description: This key's monthly request quota. } X-Quota-Remaining: { schema: { type: integer } } X-Quota-Reset: { schema: { type: integer }, description: Unix timestamp when the monthly quota resets. } content: application/rss+xml: { schema: { type: string } } application/atom+xml: { schema: { type: string } } application/feed+json: { schema: { type: string } } NotModified: description: The caller's cached copy (If-None-Match / If-Modified-Since) is still current. BadRequest: description: Missing or invalid parameters. content: { text/plain: { schema: { type: string } } } Unauthorized: description: Missing, invalid, or inactive bearer API key. content: { text/plain: { schema: { type: string } } } Forbidden: description: The upstream source is private, restricted, or otherwise not publicly viewable. content: { text/plain: { schema: { type: string } } } NotFound: description: The requested channel, subreddit, repository, account or hashtag does not exist. content: { text/plain: { schema: { type: string } } } TooManyRequests: description: Per-minute burst limit or monthly quota exceeded. headers: X-RateLimit-Limit: { schema: { type: integer } } X-RateLimit-Remaining: { schema: { type: integer } } X-RateLimit-Reset: { schema: { type: integer } } X-Quota-Limit: { schema: { type: integer } } X-Quota-Remaining: { schema: { type: integer } } X-Quota-Reset: { schema: { type: integer } } content: { text/plain: { schema: { type: string } } } LegalRestriction: description: Unavailable for legal/compliance reasons (451). content: { text/plain: { schema: { type: string } } } BadGateway: description: The upstream source could not be fetched or parsed. content: { text/plain: { schema: { type: string } } } schemas: NewAPIKey: type: object properties: id: { type: string } key: { type: string, description: 'Raw bearer key (ibk_...). Shown once — not recoverable afterward.' } status: { type: string, enum: [active] } created_at: { type: string, format: date-time } APIKey: type: object properties: id: { type: string } status: { type: string, enum: [active, revoked] } key_last4: { type: string } created_at: { type: string, format: date-time } APIKeyUsage: type: object properties: id: { type: string } status: { type: string, enum: [active, revoked] } key_last4: { type: string } usage: { type: integer } quota: { type: integer } month: { type: string, description: 'YYYY-MM (UTC).' }