Ironfang Jobs API

The Jobs API from Ironfang — 4 operation(s) for jobs.

Operations 6

POST /v1/jobs Submit a durable render job #
GET /v1/jobs List jobs #
GET /v1/jobs/{id} Poll a job #
DELETE /v1/jobs/{id} Cancel a job #
GET /v1/jobs/{id}/result Collect a job's result #
GET /v1/results/{exp}/{sig} Signed result download #

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-jobs-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-jobs-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Ironfang Render Jobs 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: Jobs
  description: Asynchronous jobs - status, result, cancellation.
paths:
  /v1/jobs:
    post:
      tags:
      - Jobs
      summary: Submit a durable render job
      description: 'The same renders as the synchronous endpoints - `kind` is one of

        `screenshot`, `pdf`, `qr`, `image`, `clip` or `site_preview` and

        `request` is that endpoint''s body (an `image` job names its

        `template` inside `request`) - accepted with `202` and run by a

        worker. Poll the job, or cancel it, then collect the result while it

        is hosted: 24 hours, private, through a signed link valid for 15

        minutes. A process restart after the `202` never loses the job.


        Send `Idempotency-Key` on every submission. The same key with the same

        request returns the existing job with `200`; the same key with a

        different request is a `409 idempotency_conflict`. `external_id` is

        your own reference, up to 120 characters.


        Billing: the most the job can count is reserved on your billing

        account when it is accepted - in the same step, so a job you were told

        about always holds its reservation, and a refusal leaves no job. The

        one reservation is kept across retries of the job and committed, with

        what the job produced, when it succeeds; a site preview reserves the

        60 second limit and commits its real length. A job that fails,

        is cancelled while queued, or is cancelled while running without a

        usable output, counts nothing. The job''s `usage` says what it holds:

        `{"meter", "reserved", "committed"}`.


        Capacity: an account may have 1,000 unfinished jobs (queued, running

        or being cancelled); a job past that is `429 queue_full` and is not

        created. A job that uses the browser runs in one of the account''s 2

        browser slots, shared with its synchronous renders; while both are

        busy it waits in the queue rather than being refused.'
      operationId: submitJob
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - kind
              - request
              properties:
                kind:
                  type: string
                  enum:
                  - screenshot
                  - pdf
                  - qr
                  - image
                  - clip
                  - site_preview
                request:
                  type: object
                  description: The synchronous endpoint's request body for that kind.
                external_id:
                  type: string
                  maxLength: 120
                delivery:
                  $ref: '#/components/schemas/JobDelivery'
      responses:
        '202':
          description: The job was accepted; the body is the job resource.
        '200':
          description: The same Idempotency-Key and request were seen before; this is that job.
        '400':
          description: Invalid submission.
        '403':
          description: The key carries no `render:render` scope, or is not account-backed.
        '404':
          description: The template named by an image job does not exist.
        '409':
          description: '`idempotency_conflict`: the key was used with a different request.'
        '429':
          $ref: '#/components/responses/RateLimited'
        '402':
          $ref: '#/components/responses/BillingRefused'
        '503':
          $ref: '#/components/responses/BillingUnavailable'
        '501':
          description: This deployment cannot render clips or site previews.
    get:
      tags:
      - Jobs
      summary: List jobs
      description: Newest first. `status` filters; `active` includes queued, running and cancellation_requested. `limit` up to 100; pass `next_cursor` back to continue.
      operationId: listJobs
      parameters:
      - name: status
        in: query
        schema:
          type: string
      - name: limit
        in: query
        schema:
          type: integer
          maximum: 100
      - name: cursor
        in: query
        schema:
          type: string
      responses:
        '200':
          description: Jobs and `next_cursor`.
        '400':
          description: '`bad_request`: `cursor` is not one this endpoint issued.'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: The key carries no `render:render` scope, or is not account-backed.
  /v1/jobs/{id}:
    get:
      tags:
      - Jobs
      summary: Poll a job
      description: '`status` is `queued`, `running`, `cancellation_requested`,

        `succeeded`, `failed` or `cancelled`. A succeeded job carries

        `result` with `content_type`, `bytes`, `sha256`, `expires_at` and,

        while hosted, a signed `url` valid for 15 minutes. A failed job

        carries `error.code` and `error.message`. `usage` names the meter

        and the quantity reserved and committed. The submitted request is

        never returned; `request_summary` is the redacted shape.'
      operationId: getJob
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The job resource.
        '404':
          description: No such job on this account.
    delete:
      tags:
      - Jobs
      summary: Cancel a job
      description: A queued job is cancelled and its reservation released at once; a running one stops at its next safe point, and counts only if it had already produced a usable output.
      operationId: cancelJob
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The job, cancelled or with cancellation requested.
        '404':
          description: No such job on this account.
        '409':
          description: '`job_finished`: nothing left to cancel.'
  /v1/jobs/{id}/result:
    get:
      tags:
      - Jobs
      summary: Collect a job's result
      description: 'Redirects (`302`) to a signed download link valid for 15 minutes; the

        link needs no API key. The hosted result is kept for 24 hours after

        success, privately, on Ironfang''s side - not permanent hosting.'
      operationId: getJobResult
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '302':
          description: Redirect to the signed download link.
        '404':
          description: No such job on this account.
        '409':
          description: '`result_not_ready`: the job has not succeeded.'
        '410':
          description: '`result_expired`: the hosted result is gone; run the job again.'
  /v1/results/{exp}/{sig}:
    get:
      tags:
      - Jobs
      security: []
      summary: Signed result download
      description: The link a job's `result.url` points at. The signature is the authorisation; it expires after 15 minutes.
      operationId: getSignedResult
      parameters:
      - name: exp
        in: path
        required: true
        schema:
          type: string
      - name: sig
        in: path
        required: true
        schema:
          type: string
      - name: job
        in: query
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The result bytes with their recorded content type.
        '403':
          description: Bad or expired signature.
        '410':
          description: The hosted result has expired.
components:
  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'
    Unauthorized:
      description: Missing, malformed, revoked or unknown API key.
      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
  schemas:
    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
    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
  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