Dataset

Create a dataset and get a URL to upload it to

Creates a dataset AND its first upload session in one call — a presigned URL your client PUTs the file to directly, no API credentials involved in that PUT. Call POST /datasets/{datasetId}/uploads/{uploadId}/finalize once the upload completes to kick off ingest.

Losing this response loses nothing: calling this dataset's POST /datasets/{datasetId}/uploads returns the very same upload session again rather than opening a new one, as long as nothing has been finalized against it yet.

v1 is ticker data only — type is not a request field, it is always "ticker" in the response. instrument must be a plain spot pair (BASE/QUOTE, exactly one /); derivative forms (e.g. BTC/USDT:USDT) are rejected.

Upload format. A CSV with a header row, a parquet file with the same columns by name, or a lastra file — our own native columnar format, the same one a dataset's dataUrl hands back by default, so a downloaded dataset can be handed to another user to upload with no conversion in between. For CSV/parquet, required: timestamp (ISO-8601, or numeric epoch seconds/millis/micros — detected from the first row, then enforced for every later row), close. Optional: open, high, low, volume, quoteVolume, bid, bidSize, ask, askSize. A lastra upload carries its own fixed column set instead and only needs a timestamp series and a close series present. Cadence and timestamp unit are discovered from the data, not declared, for all three.

A CSV upload is converted to our native columnar format (lastra) for storage. A parquet or lastra upload is stored as-is today. Either way, always check dataFormat on GET /datasets/{datasetId} and GET /datasets/{datasetId}/uploads/{uploadId} for which one dataUrl actually is, rather than assuming from how you uploaded it (a converted CSV and an uploaded lastra file both report dataFormat: "lastra").

The bytes PUT to upload.url may be that file directly, gzipped (.gz), or zipped (.zip, exactly one file inside — a dataset is one file regardless of how it travels). Format is detected from the decompressed content itself: there is no filename or Content-Type anywhere in this flow for a client to declare it with, so nothing needs to be sent besides the bytes.

post/datasets

Request

  • Base URL: https://api.qtsurfer.net/v1
  • URL: https://api.qtsurfer.net/v1/datasets
  • Auth: HTTP bearer

Request body

namestring required

A name unique among your datasets. 409 if already taken.

instrumentstring required

Exchange instrument identifier (e.g. a currency pair)

Example request

{
  "name": "My BTC ticks",
  "instrument": "BTC/USDT"
}

Response

Dataset created, with an upload session ready for its first version

datasetIdstring required

Opaque id of the newly created dataset.

namestring required

Unique name of the newly created dataset.

type'ticker' required

Always ticker in v1.

instrumentstring required

Exchange instrument identifier (e.g. a currency pair)

uploadIdstring required

Identifies this upload session. Pass to POST /datasets/{datasetId}/uploads/{uploadId}/finalize once the PUT completes.

Example response

{
  "datasetId": "ds_3f9a1c2e7b0d4a5f",
  "name": "My BTC ticks",
  "type": "ticker",
  "instrument": "BTC/USDT",
  "uploadId": "up_1a2b3c4d5e6f7a8b",
  "upload": {
    "url": "https://storage.qtsurfer.com/uploads/00000000-.../up_1a2b3c4d5e6f7a8b/raw.csv?X-Amz-...",
    "expiresInMinutes": 15
  }
}

Changes

No recorded changes to this operation across all 50 revisions of this API.