Ironfang Batches API

Up to 100 renders submitted, and refused, together.

Operations 3

GET /v1/batches List batches #
POST /v1/batches Submit up to 100 jobs together #
GET /v1/batches/{id} Poll a batch #

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/ironfang:ironfang-batches-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

ironfang-batches-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Ironfang Render Batches API
  version: 1.0.0
  summary: Screenshots, PDFs, dynamic images and video through one API.
  description: Ironfang Render renders web pages to images, PDFs and video.
  contact:
    name: Ironfang
    email: hello@ironfang.uk
    url: https://ironfang.com
  termsOfService: https://ironfang.com/legal/terms
  license:
    name: Proprietary - use governed by the Ironfang terms of service
    url: https://ironfang.com/legal/terms
servers:
- url: https://api.ironfang.com/render
  description: Production
- url: https://api.ironfang.com
  description: Production, unpartitioned - retained for existing clients
security:
- apiKey: []
tags:
- name: Batches
  description: Up to 100 renders submitted, and refused, together.
paths:
  /v1/batches:
    get:
      operationId: listBatches
      summary: List batches
      description: Tenant-scoped metadata, newest first by creation time and ID. Equal timestamps retain stable pagination. Newer insertions do not appear on subsequent pages; refresh the first page to see them. No input payloads or child job bodies in batch summaries. A final page has an empty next_cursor. Responses are no-store. The JWT portal twin has the same response and pagination contract.
      tags:
      - Batches
      parameters:
      - name: limit
        in: query
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 50
      - name: cursor
        in: query
        schema:
          type: string
          maxLength: 256
        description: Pass next_cursor from the preceding page; empty means the first page.
      responses:
        '200':
          description: One bounded page.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BatchList'
        '400':
          description: Malformed cursor, repeated/unknown parameter or limit outside 1–100.
        '401':
          description: Authentication required.
        '403':
          description: The caller cannot read this collection.
        '500':
          description: Storage unavailable.
    post:
      tags:
      - Batches
      summary: Submit up to 100 jobs together
      description: 'One `default` request plus `items` that override parts of it, merged

        one level deep: an item''s field wins, everything else comes from the

        default. The default is written for the batch''s `kind`, so it applies

        only to items of that kind; an item that names a different kind stands

        on its own request, and any item may carry its own `delivery`. Every item is validated as a single job would be,

        and an unknown field is refused rather than ignored, because a typo

        that is silently dropped is a render spent on the wrong thing.


        The batch is accepted or refused whole. Every item is validated before

        any is stored, and every item is admitted on your billing account in

        one step, so a batch any item of which cannot be admitted is refused

        with the billing code, reserves nothing and leaves no jobs behind.

        Each item then commits or releases on its own as its job finishes.

        A validation error names the item that was wrong. A batch that would

        take the account past 1,000 unfinished jobs is refused whole the

        same way, with `429 queue_full`.


        Items are ordinary jobs: poll them individually, or poll the batch for

        the aggregate. There is no ZIP of results - configure a storage

        destination if you want the output collected in one place.


        Send `Idempotency-Key` to make the submission safe to retry: the same

        key returns the batch it first created, with its jobs and another

        `202`, instead of a second set of jobs and a second reservation. The

        request is not compared with the first. Poll the batch for its

        current counts.'
      operationId: submitBatch
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - items
              properties:
                kind:
                  type: string
                  enum:
                  - screenshot
                  - pdf
                  - qr
                  - image
                  - clip
                  - site_preview
                default:
                  type: object
                  description: The request every item starts from.
                delivery:
                  $ref: '#/components/schemas/JobDelivery'
                external_id:
                  type: string
                  maxLength: 120
                items:
                  type: array
                  minItems: 1
                  maxItems: 100
                  items:
                    type: object
                    properties:
                      kind:
                        type: string
                      request:
                        type: object
                      external_id:
                        type: string
                        maxLength: 120
                      delivery:
                        $ref: '#/components/schemas/JobDelivery'
      responses:
        '202':
          description: The batch was accepted; the body carries the batch and one job per item.
        '400':
          description: Invalid submission, or `batch_too_large`. The message names the item.
        '403':
          description: The key carries no `render:render` scope, or is not account-backed.
        '429':
          $ref: '#/components/responses/RateLimited'
        '402':
          $ref: '#/components/responses/BillingRefused'
        '503':
          $ref: '#/components/responses/BillingUnavailable'
  /v1/batches/{id}:
    get:
      tags:
      - Batches
      summary: Poll a batch
      description: '`counts` totals the items by state and `done` is true once none of

        them can change. `jobs` is the full job resource for every item.'
      operationId: getBatch
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The batch, its counts and its jobs.
        '404':
          description: No such batch on this account.
components:
  schemas:
    BatchList:
      type: object
      additionalProperties: false
      required:
      - batches
      - next_cursor
      properties:
        batches:
          type: array
          maxItems: 100
          items:
            $ref: '#/components/schemas/BatchSummary'
        next_cursor:
          type: string
    JobDelivery:
      type: object
      description: 'Where this job goes when it finishes, by destination id. Register the

        destination first; credentials are never sent here.


        A webhook fires on success, failure and cancellation. Storage only

        carries a result, so it fires on success alone - and a storage

        delivery that gives up raises `render.delivery.failed` on the webhook

        destination, which is how you find out that a bucket stopped

        accepting uploads.

        '
      properties:
        webhook_destination:
          type: string
          description: Id of a `webhook` destination.
        storage_destination:
          type: string
          description: Id of an `s3` destination.
        storage_key:
          type: string
          maxLength: 700
          description: 'The object key, relative to the destination''s prefix. Fixed text

            plus `{job_id}`, `{external_id}` and `{date}` (`YYYY/MM/DD`) -

            no other field, and nothing evaluated. Segments may hold letters,

            digits, dot, dash and underscore; anything that would climb out

            of the prefix is refused when the job is submitted, not quietly

            rewritten. Defaults to `render/{date}/{job_id}`.

            '
          example: captures/{external_id}/{job_id}.png
    BatchSummary:
      type: object
      additionalProperties: false
      required:
      - id
      - external_id
      - item_count
      - created_at
      - completed_at
      - counts
      - done
      properties:
        id:
          type: string
          format: uuid
        external_id:
          type: string
        item_count:
          type: integer
          minimum: 1
          maximum: 100
        created_at:
          type: string
          format: date-time
        completed_at:
          type:
          - string
          - 'null'
          format: date-time
        done:
          type: boolean
        counts:
          type: object
          additionalProperties: false
          required:
          - total
          - queued
          - running
          - succeeded
          - failed
          - cancelled
          properties:
            total:
              type: integer
              minimum: 0
              maximum: 100
            queued:
              type: integer
              minimum: 0
              maximum: 100
            running:
              type: integer
              minimum: 0
              maximum: 100
            succeeded:
              type: integer
              minimum: 0
              maximum: 100
            failed:
              type: integer
              minimum: 0
              maximum: 100
            cancelled:
              type: integer
              minimum: 0
              maximum: 100
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
              description: 'Stable, safe to branch on. New codes may be added: treat a

                code you do not recognise as a failure of the class its HTTP

                status indicates.

                '
              enum:
              - bad_request
              - unauthorized
              - invalid_api_key
              - auth_failed
              - forbidden
              - billing_moved
              - not_found
              - rate_limited
              - target_rate_limited
              - render_failed
              - bad_signature
              - signing_disabled
              - account_gone
              - account_not_linked
              - account_required
              - admission_failed
              - bad_destination
              - bad_target
              - batch_plan_failed
              - batch_submit_failed
              - batch_too_large
              - bundle_failed
              - clip_busy
              - concurrency_limit
              - delegated_not_allowed
              - delivery_disabled
              - delivery_plan_failed
              - destination_disabled
              - idempotency_conflict
              - idempotency_key_used
              - insufficient_scope
              - invalid_token
              - job_finished
              - job_plan_failed
              - job_submit_failed
              - logo_fetch_failed
              - pdf_too_long
              - preview_busy
              - queue_full
              - result_expired
              - result_not_ready
              - result_read_failed
              - secret_failed
              - site_preview_disabled
              - store_failed
              - unknown_destination
              - unscannable
              - vault_failed
              - video_disabled
              - free_allowance_exhausted
              - account_budget_exhausted
              - product_budget_exhausted
              - paid_usage_paused
              - payment_required
              - payment_action_required
              - account_restricted
              - account_closed
              - product_not_eligible
              - exemption_limit_reached
              - operation_too_large
              - billing_temporarily_unavailable
            message:
              type: string
              description: Human-readable. May change; do not match on it.
            docs:
              type: string
              format: uri
              description: Where the error codes are documented.
        request_id:
          type: string
          description: The X-Ironfang-Request-ID of the failed request. Quote it to support.
      example:
        error:
          code: target_rate_limited
          message: too many renders for that host, try again shortly
          docs: https://ironfang.com/render/docs#errors
        request_id: 01992f6e-7c1a-7b3e-9d52-4f0a6c8e2b17
  responses:
    RateLimited:
      description: "Rate limits and exhausted billing allowances share this status,\nseparated by `error.code`:\n\n- `rate_limited` - your account exceeded 120 renders/minute\n- `target_rate_limited` - the site being rendered is being hit too hard\n  across all customers (60/minute per host)\n- `clip_busy`, `preview_busy` - every encoder or preview slot on the\n  worker is in use; `Retry-After` says when to come back\n- `concurrency_limit` - your account already has 2 browser\n  operations in progress, synchronous renders and running jobs\n  together; retry after `Retry-After`, or submit a durable job,\n  which waits for a free slot instead of being refused\n- `queue_full` - your account already has 1,000 unfinished jobs, or\n  the batch would take it past that; nothing was enqueued\n- `free_allowance_exhausted` - this month's free allowance for the\n  meter is used up and paid usage is not enabled\n- `account_budget_exhausted`, `product_budget_exhausted` - the\n  billing account's monthly limit, or Ironfang Render's, is reached\n- `exemption_limit_reached` - an exempt account's agreed limit is reached\n\nThe rate and capacity limits clear by themselves, so retry with\nbackoff, after `Retry-After` where it is sent. The billing codes do\nnot: enable paid usage or raise the limit in Billing, or wait for the\nmonth to roll over. Nothing was reserved or counted. Branch on the\ncode rather than the status.\n"
      headers:
        Retry-After:
          description: Seconds to wait before retrying, on the refusals that clear by themselves.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    BillingRefused:
      description: 'Paid usage cannot be billed: `paid_usage_paused`, `payment_required`,

        `payment_action_required` or `account_restricted`. Resolve it in

        Billing; nothing was reserved or counted. The same body can also

        carry `account_closed` (410) and `operation_too_large` (413).

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    BillingUnavailable:
      description: '`billing_temporarily_unavailable` - the billing ledger could not admit

        the request just now. Retry after `Retry-After` seconds; nothing was

        reserved.

        '
      headers:
        Retry-After:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: 'Makes a submission safe to retry. Any string up to 255 characters;

        surrounding whitespace is ignored and a longer key is a `400

        bad_request`. A key is remembered per account for as long as the job

        it created exists, and has no expiry of its own. Jobs and batches

        keep separate key spaces.


        A job submitted again under its key returns the existing job with

        `200` when the request is the same, and is a `409

        idempotency_conflict` when it differs. A batch submitted again under

        its key returns the batch the key first created, with its jobs and

        `202`; the request is not compared, so a batch has no conflict

        response.

        '
      schema:
        type: string
        maxLength: 255
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: 'An API key from the portal, sent as `Authorization: Bearer rw_live_...`.

        '
externalDocs:
  description: Ironfang Render documentation
  url: https://ironfang.com/render/docs