Table of Contents

POST /v1/generate-table

Generates data for one table, returned as JSON or as CSV. The response has no manifest and no schema.

To validate a request and see its estimated cost without generating data, send the same body to POST /v1/estimate-table.

Request

The body is one table, with the table properties name, rowCount, fields and primaryKey (see Building a Request), and the dataset-level properties $schema, csvFormat, schemaVersion and seed, all at the top level:

Property Type Default Description
$schema string? none URL of a JSON Schema for the request, used by an editor to validate and complete a request file. Accepted and ignored.
csvFormat CsvFormatRequest see CSV formatting Formatting settings for the CSV.
fields FieldRequest[] required The fields that generate the table's columns. Must contain at least one field. See Building a Request.
name string required Table name.
primaryKey string[]? none Columns that form the table's primary key, in key order. The columns are checked, but the response has no schema to declare the key in. See Declaring a primary key.
rowCount integer required Number of rows to generate. At least 1.
schemaVersion string "1.0" Version of the request contract the request uses.
seed integer? random Seed for the request. The same request with the same seed produces the same data.
curl -X POST https://api.alchemy.example/v1/generate-table \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  --data @request.json

Response

The default response is JSON, with name, rowCount, columns, the table's data as a csv string, and the amount charged. With Accept: text/csv, the response is the CSV data only: the header row and every generated row, written with the request's csvFormat or the defaults. The Content-Type is text/csv for the default UTF-8 encoding. Any other encoding adds its charset, so Utf8Bom returns text/csv; charset=utf-8, and both UTF-16 encodings return text/csv; charset=utf-16:

curl -X POST https://api.alchemy.example/v1/generate-table \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: text/csv" \
  -o customer.csv \
  --data @request.json

Content negotiation follows the same rules as POST /v1/generate-dataset: the q= quality values determine the type when Accept lists both types, on a tie the type listed first is used, and any other Accept value, or no Accept header, returns JSON.

Every response includes the X-Alchemy-Generated-Bytes header. See Getting Started for billing.

A billed request also returns the amount charged, in the X-Alchemy-Cost and X-Alchemy-Currency headers and, in a JSON response, in the cost and currency properties. See What the call cost.

Errors

All error responses are RFC 7807 ProblemDetails bodies. type is a fixed urn:alchemy:error:... identifier for the error. Use type to identify an error, rather than title or detail, which are text and can change.

400, because the request body is missing:

{
  "type": "urn:alchemy:error:missing-request-body",
  "title": "Request body is required.",
  "status": 400
}

400, because the body is not valid JSON, or contains a property that does not exist. Unknown properties are rejected, not ignored. detail gives the JSON path of the property:

{
  "type": "urn:alchemy:error:malformed-request-body",
  "title": "Request body could not be read.",
  "status": 400,
  "detail": "Unknown property 'country' at $.fields[0].country. Check it against the properties that type accepts. A property of a nested object must be written inside that object."
}

400, because a field's settings are invalid. In this example, map contains a sub-field that the field type does not have:

{
  "type": "urn:alchemy:error:invalid-generation-request",
  "title": "Invalid generation request.",
  "status": 400,
  "detail": "map names sub-field 'x', which a Boolean field does not have. Its sub-fields are value.",
  "path": "$.fields[1]",
  "table": "customers",
  "fieldIndex": 1,
  "fieldType": "Boolean",
  "columns": ["is_active"]
}

When the response identifies the field, path is its location in the request body, table is the name of its table, fieldIndex is its position in fields, fieldType is its type, and columns lists the columns its map writes to. Some field errors, such as two boundingLocation filters that cannot be combined, do not include these properties.

401, because the API key is missing or invalid:

{
  "type": "urn:alchemy:error:unauthorized",
  "title": "Missing or invalid API key.",
  "status": 401
}

402, because the balance does not cover the cost of the request:

{
  "type": "urn:alchemy:error:insufficient-credit",
  "title": "Insufficient credit.",
  "status": 402,
  "detail": "This call costs 0.0042 USD and your balance is 0.001 USD. If automatic top-up is enabled one has been requested. Retry shortly. Otherwise, top up to continue."
}

413, because the request body is larger than the maximum size. detail states the maximum:

{
  "type": "urn:alchemy:error:request-body-too-large",
  "title": "Request body is too large.",
  "status": 413
}

400, because a field's type is not a field type:

{
  "type": "urn:alchemy:error:malformed-request-body",
  "title": "Request body could not be read.",
  "status": 400,
  "detail": "Unknown field type 'Foo' at $.fields[0]."
}

503, because the service is busy. The Retry-After header gives the number of seconds to wait before retrying. See When the service is busy:

{
  "type": "urn:alchemy:error:service-busy",
  "title": "The service is busy.",
  "status": 503
}

503, because the reference data the request reads is temporarily unavailable. The Retry-After header gives the number of seconds to wait before retrying. See When the service is busy:

{
  "type": "urn:alchemy:error:reference-data-unavailable",
  "title": "Reference data is temporarily unavailable.",
  "status": 503
}

503, because billing is temporarily unavailable. Retry the request later:

{
  "type": "urn:alchemy:error:billing-unavailable",
  "title": "Billing is temporarily unavailable. Please try again shortly.",
  "status": 503
}

A request that returns an error is not charged.

Example

{
  "seed": 42,
  "name": "widget",
  "rowCount": 5,
  "fields": [
    {
      "type": "Number",
      "mode": "Sequential",
      "start": 100,
      "map": { "value": { "column": "id", "dataType": "Integer" } }
    }
  ]
}

Sample output:

id
100
101
102
103
104