API reference

Base URL: https://api.ecomsolo.com. All endpoints live under /v1, require the read_data scope, and authenticate with Authorization: Bearer esk_live_....

GET /v1/account

Verify a key and see the account. Not plan-gated — it answers even when the API is not enabled, which makes it the right diagnostic.

Response

{
  "account_id": "acct_9f3c...",
  "account_name": "Acme Retail Group",
  "subscription_id": "sub_2a71...",
  "api_enabled": true,
  "store_count": 3,
  "scopes": ["read_data"]
}
FieldTypeMeaning
account_idstringThe account identifier.
account_namestringThe account's display name.
subscription_idstringThe account's subscription identifier.
api_enabledbooleanWhether the plan includes the API.
store_countnumberHow many stores the key can access.
scopesstring arrayThe scopes granted to the key.

GET /v1/stores

The stores the key can access.

Response — an array of:

FieldTypeMeaning
idstringStore identifier (use in store_ids).
namestringStore name (use in WHERE store IN (...)).
myshopify_domainstringThe store's myshopify.com domain.
currencystringThe store's currency, ISO code.
timezonestringThe store's IANA time zone.
statusstringactive or syncing.

GET /v1/datasets

The self-describing catalog of datasets and fields. See Datasets and fields.

Response — an array of datasets:

[
  {
    "name": "sales",
    "title": "Sales",
    "description": "Consolidated sales across all your stores.",
    "fields": [
      {
        "name": "total_sales",
        "title": "Total sales",
        "description": "...",
        "type": "money",
        "role": "metric",
        "default_aggregation": "sum"
      }
    ]
  }
]

Field type is one of string, number, money, date, boolean, percent. Field role is dimension or metric. default_aggregation is one of sum, avg, min, max, count, count_distinct, or null for dimensions.

POST /v1/query

Run a SoloQL query. See Querying with SoloQL.

Request body

FieldTypeMeaning
querystring, requiredThe SoloQL query string.
store_idsstring array, optionalNarrow to these store IDs; can only narrow within the key's scope, never widen.
time_zonestring, optionalIANA zone; defaults to the account's zone.

Response

{
  "columns": [
    { "name": "store", "title": "Store", "type": "string", "role": "dimension" }
  ],
  "rows": [
    { "store": "EU Shop", "total_sales": 48210.55 }
  ],
  "totals": { "total_sales": 120144.65 },
  "currency": "EUR",
  "time_zone": "Europe/Athens",
  "window": { "gte": "2026-06-24T00:00:00Z", "lt": "2026-07-24T00:00:00Z" }
}
FieldMeaning
columnsOne entry per column: name, title, type, role. Rows are keyed by name.
rowsThe result rows.
totalsPresent only with WITH TOTALS.
currencyISO currency the money columns were converted to.
time_zoneIANA zone the window resolved in.
windowResolved UTC range (gte, lt).

Send Accept: text/csv to get the same tabular result as CSV, with the column titles as the header row.

Error model

Errors use RFC 7807 problem+json, with Content-Type: application/problem+json:

{
  "type": "about:blank",
  "title": "Invalid query",
  "status": 400,
  "detail": "The query has one or more errors.",
  "errors": [
    { "line": 1, "column": 13, "message": "Unknown field 'totl_sales'", "severity": "error" }
  ]
}
StatusWhenNotes
400Invalid query — empty, or SoloQL with errors.For SoloQL errors the body includes errors, an array of positioned diagnostics (line, column, message, severity).
401Unauthorized.Missing, malformed, unknown, or revoked key.
403Forbidden."Insufficient scope" (key lacks read_data) or "Plan upgrade required" (plan does not include the API). GET /v1/account still works and reports api_enabled: false.
429Rate limit exceeded.Includes Retry-After (seconds) and X-RateLimit-Limit headers. See Rate limits.