---
title: "API overview"
description: "Convert spreadsheets, CSV, JSON and MySQL dumps into SQL from your own code with the IntoSQL API: authentication, the conversion flow, limits and idempotency."
canonical: https://intosql.com/docs/api
last_updated: 2026-10-15
---

# API overview

The IntoSQL API converts files on IntoSQL's servers with the same engine as the converter on the site. You send a file, or the address of one, start the conversion, and download the SQL when it's done. A MySQL or MariaDB dump (`.sql` or `.sql.gz`) converts the same way into a PostgreSQL script ([MySQL to PostgreSQL](/docs/migrations/mysql-to-postgresql)). A webhook can tell you when that happens.

> **Pro:** The API is part of IntoSQL Pro. Each month includes 5 GB of files converted on IntoSQL's servers.

## Base address

Every endpoint lives under one address, and every request and answer is JSON.

```text
https://intosql.com/api/v1
```

The OpenAPI 3.1 document at [/api/v1/openapi.json](/api/v1/openapi.json) describes every endpoint, field and error. Point an API client or a code generator at it.

## Authentication

Make a key in [Settings, API keys](/settings/api-keys) and send it as a bearer token. A key belongs to your account: anyone who has it can convert files on your plan, so keep it on the server and out of browsers and repositories.

```shell
curl https://intosql.com/api/v1/usage \
  -H "Authorization: Bearer $INTOSQL_API_KEY"
```

A request without a key, or with one that was deleted, gets `401` with the code `unauthenticated`. An account without Pro gets `402` with `needs_pro`.

## How a conversion goes

1. **Open a conversion.** `POST /conversions` with the file's `name`, its size in `bytes` and the [conversion spec](/docs/api/spec). The answer has an `upload` address, valid for an hour.
2. **Upload the file.** `PUT` the file's bytes to `upload.url` with the headers in `upload.headers`, exactly as given.
3. **Start it.** `POST /conversions/{id}/start`. The conversion is `queued`, then `running`, then `done` or `failed`. `POST /conversions/{id}/cancel` stops it on the way.
4. **Read the result.** `GET /conversions/{id}` until `status` is `done`; `output.url` then downloads the SQL for ten minutes. Read the conversion again for a fresh address.

To skip the upload, send `url` instead of `name` and `bytes`: a public HTTPS address of the file. IntoSQL downloads it and starts right away, and the answer is `202`.

```shell
curl -X POST https://intosql.com/api/v1/conversions \
  -H "Authorization: Bearer $INTOSQL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/exports/ventas.csv", "spec": {"output": {"dialect": "postgresql"}}}'
```

Each [SDK](/docs/sdks), in TypeScript, PHP, Python, Java, Go, .NET or Ruby, does all four steps in one call.

## Files and how long they stay

- An upload takes files up to 5 GiB, the most one `PUT` to storage holds. A file sent by `url` can be up to 50 GiB.
- The uploaded file and the output are deleted 24 hours after the conversion ends. `DELETE /conversions/{id}` deletes them right away.
- A failed conversion keeps its upload for those 24 hours, so you can [send it to us](/docs/api/conversions#send-us-a-conversion) if it didn't convert well.
- Each conversion's `expires_at` says when its files go.

## Limits

| Limit                                 | Value                           | Answer when reached          |
| ------------------------------------- | ------------------------------- | ---------------------------- |
| Requests per key                      | 120 a minute                    | `429` with `rate_limited`    |
| Conversions queued or running at once | 5 per account                   | `429` with `too_many_active` |
| Files converted on IntoSQL's servers  | 5 GB a month on Pro             | `402` with `over_quota`      |
| One file                              | 5 GiB uploaded, 50 GiB by `url` | `413` with `too_big`         |

[`GET /usage`](/docs/api/usage) tells you how much of this month's allowance is left.

## Retrying safely

Opening and starting a conversion accept an `Idempotency-Key` header. Send a unique value, such as a UUID, and use the same one when you retry after a timeout or a lost connection: the retry gets the first request's answer instead of opening a second conversion. Reusing a key for a different request gets `422` with `idempotency_mismatch`; retrying while the first request still runs gets `409` with `request_in_progress`.

## Errors

Every error has the same shape: a code that never changes, for your code to read, and a message for people.

```json
{
    "error": {
        "code": "invalid_request",
        "message": "Some fields are missing or invalid.",
        "fields": {
            "spec": ["The spec field is required."]
        },
        "request_id": "req_01k7r3jb2c4e6g8j0m2p4r6t8v"
    }
}
```

[Errors](/docs/api/errors) lists every code with its status and what to do.

## Webhooks

Instead of reading a conversion until it ends, save a webhook address in Settings, API keys. IntoSQL calls it when a conversion is done, failed or canceled, signed so you can check it came from IntoSQL. See [Webhooks](/docs/api/webhooks).

## Versions

The API is versioned in its path: `/api/v1`. Within v1, changes only add: new fields in answers, new error codes, new endpoints, new optional fields in requests. Write your code to ignore fields it doesn't know and to treat an unknown error code like its HTTP status.

Nothing in v1 is renamed or removed without notice. Before an endpoint or field goes, its answers carry a `Deprecation` header and a `Sunset` header with the date it stops working, at least six months ahead, and the change is listed here. A change that can't be made by adding gets a new version, `/api/v2`, with v1 kept alongside.

Every answer carries an `X-Request-Id`. Quote it when you write to us about a request.
