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.
Request
- Base URL: https://api.qtsurfer.net/v1
- URL: https://api.qtsurfer.net/v1/datasets
- Auth: HTTP bearer
Request body
Example request
{
"name": "My BTC ticks",
"instrument": "BTC/USDT"
}Response
Dataset created, with an upload session ready for its first version
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.