Table of Contents

POST /v1/generate-dataset

Generates data for one or more tables. A table can reference another table in the same request.

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

Request

The body is a DatasetRequest. See Building a Request for its structure.

curl -X POST https://api.alchemy.example/v1/generate-dataset \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  --data @request.json

Response

The default response is a DatasetResponse in JSON. See Reading the Response. With Accept: application/x-csvx, the response is a .csvx package, a ZIP file containing a manifest, a schema file and one CSV file per table. See Schema format and Accept for how schemaFormat and schemaName apply to each format.

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

When the Accept header lists both media types, standard HTTP content negotiation applies: the q= quality values determine the type, and on a tie the type listed first is used. Any other Accept value, or no Accept header, returns JSON.

Every response includes the X-Alchemy-Generated-Bytes header, the uncompressed size of the generated data. 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 body has no tables:

{
  "type": "urn:alchemy:error:empty-table-list",
  "title": "Request must contain at least one table.",
  "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": "$.tables[0].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 that table's 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 $.tables[0].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,
  "schemaFormat": "Xml",
  "schemaName": "customer_export",
  "tables": [
    {
      "name": "customer",
      "rowCount": 5,
      "fields": [
        {
          "type": "Number",
          "mode": "Sequential",
          "start": 1,
          "map": { "value": { "column": "id", "dataType": "Integer" } }
        },
        {
          "type": "Person",
          "countries": [{ "country": "us" }],
          "map": {
            "firstName": { "column": "first_name", "dataType": "VarChar" },
            "lastName": { "column": "last_name", "dataType": "VarChar" }
          }
        }
      ]
    }
  ]
}

schemaFormat and schemaName are optional, with defaults of "Json" and "dataset". With Accept: application/x-csvx, this request's package contains schema.xml, and the schema's name is "customer_export". See Schema format and Accept.