> ## Documentation Index
> Fetch the complete documentation index at: https://docs.abconvert.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Retrieve an export job

> Returns the job's current status. `url` holds the signed download
link while the file is still available, which means the job is
`completed` and `expires_at` is in the future. Once `expires_at`
passes, the job stays `completed` and `url` goes null. Branch on
`url`, not on `status`.




## OpenAPI

````yaml /api-reference/openapi.yaml get /exports/{id}
openapi: 3.1.0
info:
  title: ABConvert Public API
  version: 1.0.0-beta
  description: |
    Create, read, update, and run A/B tests on your Shopify store from your
    own code.

    This version covers test CRUD, lifecycle actions, archiving, results,
    order exports, scheduling, and token scopes. There are no webhooks: to
    see a test's current state, call
    [`GET /experiments/{id}`](/api-reference/experiments/retrieve-a-test).

    Every path, field, and identifier in this API spells the resource
    `experiment`. An experiment is a test: the same object you create and
    run in the ABConvert admin. These docs say "test" in prose.

    A finding is one problem the server found, carrying a `code`, a
    `message`, and a `severity`. An `error` finding blocks the request and
    returns 422. A `warning` finding rides along with a successful response,
    in `warnings`.

    Conventions shared by every endpoint (identifiers, data formats,
    pagination, sparse updates, idempotency, rate limits, per-type support,
    and feature availability) live in the
    [API overview](/api-reference/overview). Tokens and scopes live in
    [Authentication](/api-reference/authentication).
servers:
  - url: https://api.abconvert.io/v1
security:
  - bearerAuth: []
tags:
  - name: Experiments
    description: Create, read, update, and list tests.
  - name: Lifecycle
    description: Status transitions. See the state machine in each action's description.
  - name: Results
    description: Read-only views of pipeline-computed snapshots. Beta.
  - name: Exports
    description: Async order-export jobs.
paths:
  /exports/{id}:
    get:
      tags:
        - Exports
      summary: Retrieve an export job
      description: |
        Returns the job's current status. `url` holds the signed download
        link while the file is still available, which means the job is
        `completed` and `expires_at` is in the future. Once `expires_at`
        passes, the job stays `completed` and `url` goes null. Branch on
        `url`, not on `status`.
      operationId: getExport
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Export job ID (opaque string).
      responses:
        '200':
          description: The job.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExportJob'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        default:
          $ref: '#/components/responses/UnexpectedError'
      security:
        - bearerAuth: []
components:
  headers:
    XRateLimitLimit:
      description: |
        How many requests this token may make in the current 60-second window.
        Reads, writes, and result queries each have a separate budget, so this
        is the budget for the class this request falls in.
      schema:
        type: integer
    XRateLimitRemaining:
      description: Requests left in the current window.
      schema:
        type: integer
    XRateLimitReset:
      description: Unix timestamp when the window resets.
      schema:
        type: integer
  schemas:
    ExportJob:
      type: object
      required:
        - object
        - id
        - status
      properties:
        object:
          type: string
          const: export
        id:
          type: string
          description: Treat as opaque. Never parse it.
          example: export_9f2c1a7b3d5e4f60a1b2c3d4e5f60718
        experiment_id:
          type: string
          example: '3021'
        status:
          type: string
          enum:
            - pending
            - processing
            - completed
            - failed
          example: completed
        url:
          type:
            - string
            - 'null'
          format: uri
          description: |
            The signed download link, ready to fetch as given. Set while the
            job is `completed` and `expires_at` is still in the future; null
            before the job finishes, and null again once the link expires,
            while `status` stays `completed`. Branch on this field rather
            than on `status`.

            The link is signed, so fetching it needs no bearer token. You can
            hand the whole URL to a browser or a download tool. Anyone who has
            the URL can download the file until `expires_at`.
          example: >-
            https://app.abconvert.io/v1/exports/export_9f2c1a7b3d5e4f60a1b2c3d4e5f60718/download?expires=1787000000&signature=3f8c1d0b6a94e27d5c1b8e40f7a2d9c6b3e15f8a0d47c92e6b1a3f5d8c07e246
        expires_at:
          type:
            - string
            - 'null'
          format: date-time
        failure_reason:
          type:
            - string
            - 'null'
          description: Why the job failed. Set only when `status` is `failed`.
        created_at:
          type: string
          format: date-time
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - type
            - code
            - message
          properties:
            type:
              type: string
              enum:
                - invalid_request_error
                - authentication_error
                - permission_error
                - not_found_error
                - conflict_error
                - validation_error
                - rate_limit_error
                - api_error
            code:
              type: string
              description: |
                The code set is fixed for each error type.

                - 400: `malformed_json`, `unknown_parameter`,
                  `invalid_cursor`, `invalid_include`,
                  `invalid_idempotency_key` (an `Idempotency-Key` header that
                  is empty or over 255 characters), `invalid_request`
                  (a query parameter sent more than once, or `limit` not
                  a whole number between 1 and 100)
                - 401: `missing_token`, `invalid_token`, `token_revoked`
                - 403: `insufficient_scope`, `api_access_disabled`,
                  `subscription_inactive`, `billing_cap_reached`,
                  `feature_not_in_plan`, `carrier_service_required`
                - 404: `experiment_not_found`, `export_not_found`,
                  `results_query_not_found`
                - 409: `invalid_status_transition`, `locked_field`,
                  `idempotency_key_in_use`, `checks_pending`,
                  `editor_owned_content`
                - 413: `request_too_large` (the body is over 10 MB)
                - 422: read the `findings` array. Each entry's `param` names
                  the field and its `message` names what to fix; the top-level
                  `code` repeats the first blocking finding. Some codes aren't
                  finding rules:
                  `invalid_request` (a body or parameter the schema refuses),
                  `invalid_type` (a `type` this API can't create),
                  `invalid_configuration` (the test as stored isn't valid to
                  act on; `message` names what to fix),
                  `launch_blocked` (a launch check failed with no finding to
                  name), `preview_unsupported` (this test can't show a single
                  test group through a preview link; preview it in the
                  ABConvert admin), and
                  `export_not_available` (the test hasn't run yet, so there is
                  nothing to export).
                  A custom result query refuses its own parameters
                  with `invalid_breakdown_dimension`,
                  `breakdown_too_many_dimensions`,
                  `duplicate_breakdown_dimension`, `invalid_date_range`,
                  `date_range_out_of_bounds` (the window ends before the test
                  started, or begins after today), `sample_basis_unsupported`,
                  `product_group_not_found`,
                  `product_id_requires_product_group`, or
                  `results_not_available` (the test hasn't run yet).

                  A query parameter value outside the published vocabulary
                  (`status`, `type`, `scheduled`, a `created_at` bound) returns
                  `invalid_request` with `param` naming the parameter.

                  On create, the codes fall into four families:
                  - a resource this shop doesn't have:
                    `product_not_found`, `collection_not_found`,
                    `theme_not_found`, `template_not_found`,
                    `zone_not_found`, `rate_not_found`
                  - a test group with nothing to test, so add a change:
                    `template_required`,
                    `destination_required`, `change_required`,
                    `rate_required`, `offer_required`
                  - two parts of the request disagree, and the code names
                    which:
                    `duplicate_change`, `duplicate_group_name`,
                    `duplicate_rule_key`, `rule_key_not_found`,
                    `zone_not_declared`, `control_group_invalid`,
                    `split_sum_invalid`
                  - a value this API won't write, so author it in the
                    ABConvert admin instead:
                    `currency_mismatch`, `control_offers_rates`,
                    `match_type_unsupported`,
                    `max_discount_amount_not_allowed`,
                    `discount_combination_invalid`,
                    `condition_range_invalid`, `product_limit_exceeded`,
                    `market_pricing_incomplete`
                - 429: `rate_limited`
                - 500: `internal_error`
                - 501: `not_implemented` (a test type this API can't create
                  yet, or a test whose stored form this API can't read back;
                  use the ABConvert admin instead)
                - 503: `rate_limit_unavailable` (the limiter could not be
                  reached, so the write was refused rather than let through
                  uncounted; reads are served in this case),
                  `audit_unavailable` (the write could not be recorded in the
                  audit log, so it was not performed),
                  `idempotency_unavailable` (ABConvert could not check your
                  `Idempotency-Key`, so the request was refused rather than run
                  without replay protection; retry with the same key),
                  `internal_error` (the request could not be run alongside
                  other changes to this shop, so nothing was attempted; retry)
                - 504: `internal_error` (the write took too long and was rolled
                  back, so nothing changed)

                Every 5xx carries a `request_id`. Quote it when you contact
                support.
            message:
              type: string
            param:
              type:
                - string
                - 'null'
              description: |
                JSON path of the offending request field. On
                `feature_not_in_plan` it carries the feature slug instead:
                `price`, `offer`, `multi_market`, or `checkout_blocks`.
            request_id:
              type: string
              description: |
                Correlation id for one refused request. Present on every 5xx
                and on nothing else: a 4xx is about the request, and there is
                nothing on our side to look up. Send an `x-correlation-id`
                header and that value is the id you get back.
            details:
              type: object
              additionalProperties: true
              description: |
                Machine-readable context, present where a code defines it.
                `invalid_status_transition` carries
                `{"current_status": "...", "allowed_actions": [...]}` so a
                client can branch without parsing `message`.
            findings:
              type: array
              description: Present on `validation_error`.
              items:
                $ref: '#/components/schemas/Finding'
    Finding:
      type: object
      required:
        - severity
        - code
        - message
      properties:
        severity:
          type: string
          enum:
            - error
            - warning
        code:
          type: string
          description: |
            A snake_case identifier for the finding. Published codes never
            change, and new ones are only added.
          enum:
            - product_not_found
            - collection_not_found
            - theme_not_found
            - template_not_found
            - zone_not_found
            - rate_not_found
            - market_not_found
            - rule_key_not_found
            - template_required
            - destination_required
            - change_required
            - rate_required
            - offer_required
            - theme_required
            - market_empty
            - product_not_testable
            - theme_not_testable
            - duplicate_change
            - duplicate_group_name
            - duplicate_rule_key
            - duplicate_trigger_url
            - duplicate_theme
            - duplicate_template
            - control_group_invalid
            - control_offers_rates
            - control_redirects
            - control_runs_code
            - split_sum_invalid
            - test_groups_too_many
            - zone_not_declared
            - discount_combination_invalid
            - currency_mismatch
            - match_type_unsupported
            - max_discount_amount_not_allowed
            - condition_range_invalid
            - condition_unit_invalid
            - product_limit_exceeded
            - country_limit_exceeded
            - rule_limit_exceeded
            - market_pricing_incomplete
            - path_invalid
            - rate_name_required
            - rate_price_invalid
            - offer_title_required
            - filter_value_invalid
            - filter_operator_invalid
            - filter_key_required
            - filter_type_unsupported
            - custom_js_invalid
            - split_changed
            - country_served_by_other_market
            - country_ownership_ambiguous
            - market_not_contextual
            - trigger_overlaps
            - destination_matches_trigger
            - zone_not_configured
            - theme_not_published
            - filter_value_unrecognized
            - test_groups_missing
            - split_invalid
            - theme_test_running
            - visual_editor_conflict
            - app_embed_disabled
            - resource_claimed
            - test_overrides_personalization
            - audience_empty
            - force_assign_group_invalid
            - block_placement_invalid
            - modification_invalid
            - modification_kind_unknown
            - modification_duplicate_singleton
            - launch_check_failed
        param:
          type:
            - string
            - 'null'
          description: |
            JSON path of the field the finding points at, for example
            `shared.price.markets[0].countries[2]`. Offending values appear
            in `message`, not here.
        message:
          type: string
  responses:
    Unauthorized:
      description: |
        `authentication_error`. Send a valid token as
        `Authorization: Bearer <token>`.

        - `missing_token`: no bearer token on the request.
        - `invalid_token`: the token is malformed, does not exist, or names a
          shop ABConvert can no longer act for.
        - `token_revoked`: someone revoked this token in the ABConvert admin.
          Create a new one.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: >
        `permission_error`. The shop or the token isn't allowed to do this, so

        nothing changed.


        | Code | What to do | Where it applies |

        |---|---|---|

        | `insufficient_scope` | Use a token with the scope named in
        `details.required_scope`. | Any request |

        | `api_access_disabled` | API access is turned off for this shop.
        Contact support. | Any request |

        | `subscription_inactive` | Renew the shop's ABConvert subscription,
        then retry. | `start` and `resume` |

        | `billing_cap_reached` | Raise the shop's usage cap for the period,
        then retry. | `start` and `resume` |

        | `feature_not_in_plan` | Upgrade to the plan named in `message`.
        `param` names the feature: `price`, `offer`, `multi_market`, or
        `checkout_blocks`. | Create, update, `preview`, `start`, `resume` |

        | `carrier_service_required` | Give the shop carrier-calculated
        shipping. A Shopify plan with the feature, an existing carrier service,
        or annual ABConvert billing each satisfy it. | Shipping tests |


        `pause`, `end`, and `archive` never return entitlement errors. See

        [Feature availability](/api-reference/overview#feature-availability).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: |
        `not_found_error`. No resource with that ID belongs to this shop. Check
        the ID, and check that the token belongs to the shop that owns the
        resource: a token reaches one shop only.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: |
        `rate_limit_error`, code `rate_limited`. You sent more requests than
        your budget allows. Wait the number of seconds in `Retry-After`, then
        retry.
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds until the window resets.
        X-RateLimit-Limit:
          $ref: '#/components/headers/XRateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/XRateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/XRateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    UnexpectedError:
      description: |
        A status this operation doesn't enumerate. The body is the standard
        error shape.

        - 413 `request_too_large`: the body is over 10 MB. Send less.
        - 500 `internal_error`: something went wrong on our side. Retry. If it
          keeps failing, send support the `request_id`.
        - 503: the request was refused before it ran, so nothing on the shop
          changed. Retry it, with the same `Idempotency-Key` if you sent one.
          The code names which safeguard was unavailable:
          `rate_limit_unavailable`, `audit_unavailable`,
          `idempotency_unavailable`, or `internal_error`.
        - 504 `internal_error`: the write timed out and nothing changed. Retry.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        Bearer tokens created in the ABConvert admin under
        Settings → MCP & API Access. Scopes:
        `read_experiments` and `write_experiments` (write implies read; the
        default is read). See
        [Authentication](/api-reference/authentication) for which scope each
        request needs.

````