Skip to content

Data Explorer API

The Data Explorer API pulls EdgeComet's data into your own stack: the raw bot request log one row per fetch, the same events aggregated per page, and your Search Console pages and keywords. Every call is a read, over the same datasets the dashboard's Data Explorer shows.

Take a page of rows as JSON for a script, a warehouse loader, or an agent, or start a CSV export when you want the whole answer as a file.

Start with discovery. It returns the columns, filters, operators, and period presets each explorer accepts for this website, which is what you build a query body from. The column and filter reference lists the same catalog as static tables if you would rather not call discovery first.

Explorers

KeyDataModes
eventsBot requests recorded in the EdgeComet request pathrequests (default), unique_pages, comparison
gsc-pagesSearch Console performance per pagegsc_pages (fixed)
gsc-keywordsSearch Console performance per querygsc_keywords (fixed)

The mode sets the grain of an events query:

  • requests returns the raw log, one row per bot request.
  • unique_pages returns the same requests grouped by URL, with counts and averages per page.
  • comparison returns that per-URL aggregate for the main window against the comparison window, split by compCategory.

gsc-pages and gsc-keywords run one mode each. Omit mode for those, or pass the mode shown above; any other value is rejected.

Authentication and access

All four endpoints use the bearer token described in Overview & Authentication. They require the platform module and any role on the website, including readonly.

Discovery

GET /api/websites/{website}/data-explorer

bash
curl https://cloud.edgecomet.com/api/websites/123/data-explorer \
  -H "Authorization: Bearer eck_xxxxxxxxxxxxxxxx"

The response describes every exposed explorer and the account's limits:

json
{
  "explorers": [
    {
      "key": "events",
      "features": { "comparePeriod": false, "comparisonCategories": true },
      "modes": [
        {
          "key": "requests",
          "default": true,
          "columns": [
            {
              "key": "page",
              "title": "Page",
              "group": "URL",
              "dataType": "page",
              "sortable": true,
              "modes": ["requests"]
            }
          ],
          "filters": [
            {
              "field": "url",
              "title": "URL",
              "type": "string",
              "required": false,
              "modes": [],
              "operators": ["contains", "equals", "regex", "empty", "not_empty"],
              "valuelessOperators": ["empty", "not_empty"]
            }
          ],
          "defaults": {
            "columns": ["page", "event_date_time", "bot_name", "serve_time"],
            "sort": "event_date_time",
            "sortDir": "desc",
            "period": "30d"
          }
        }
      ],
      "periods": {
        "presets": ["24h", "7d", "14d", "28d", "30d", "90d"],
        "customFormat": "YYYY-MM-DD,YYYY-MM-DD",
        "timeBounds": true,
        "maxRangeDays": 365
      }
    }
  ],
  "limits": {
    "perPageMax": 500,
    "perPageDefault": 100,
    "exportRowCap": 1000000
  }
}

Read the parts you need from it rather than hardcoding them:

  • modes[].columns[].key are the values columns and sort accept.
  • modes[].filters[].field and .operators are the values a filter entry accepts. An operator listed in valuelessOperators takes no val.
  • periods.presets lists the presets this explorer serves. Search Console data is whole-day, and 24h appears for events only.
  • periods.timeBounds says whether a custom range may carry a time of day.

Filters whose values come from a fixed set carry an options array of {value, label} pairs. Column keys starting with _ are companion values that ride along in a row (a page's title next to its URL); they are not selectable.

Every key discovery can return is also listed in the column and filter reference.

Query

POST /api/websites/{website}/data-explorer/{explorerKey}/query

bash
curl -X POST https://cloud.edgecomet.com/api/websites/123/data-explorer/events/query \
  -H "Authorization: Bearer eck_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "requests",
    "period": "7d",
    "columns": ["event_date_time", "page", "status_code"],
    "filters": [
      { "field": "status_code", "op": "eq", "val": "404" }
    ],
    "sort": "event_date_time",
    "sortDir": "desc",
    "perPage": 100
  }'

An empty body {} is valid and runs the explorer's defaults.

Request body

FieldTypeNotes
modestringOptional. Defaults to the explorer's default mode. Fixed-mode explorers accept only their own mode.
columnsstring[]Optional. Column keys from discovery. Defaults to the mode's default columns. An empty array is rejected; omit the key to get defaults.
filtersobject[]Optional. Each entry takes field, an optional op, and the value keys the operator needs (val, and val2 for a range).
periodstringOptional. A preset (24h, 7d, 14d, 28d, 30d, 90d) or a custom range YYYY-MM-DD,YYYY-MM-DD. Maximum 365 days.
compPeriodstringOptional. The comparison window, same formats as period but date-only. Accepted only where the mode has a comparison window. Defaults to the equal-length window immediately before period.
compCategorystringOptional. new, returning, or lost. The comparison mode of the events explorer only.
sortstringOptional. A column key. Defaults to the mode's default sort.
sortDirstringOptional. asc or desc. Defaults to desc whenever you pass sort yourself.
pageintegerOptional. 1-based. Default 1. A page past the last one is rejected.
perPageintegerOptional. 1 to 500. Default 100.

Set the window with period, not with a filter entry: a period entry inside filters is rejected rather than applied twice.

The API refuses what it cannot honour instead of substituting something workable. An unknown body key, an unknown column, an invalid sortDir, a sub-day preset on a whole-day explorer, and a perPage above the maximum are all 400 VALIDATION_FAILED with the offending parameter named in field.

Response (200)

json
{
  "data": [
    {
      "event_date_time": "2026-08-20 14:03:11",
      "page": "https://example.com/product/42",
      "_title": "Product 42",
      "_status_code": 404,
      "status_code": 404,
      "_rowId": "a1b2c3d4e5f6"
    }
  ],
  "meta": {
    "explorerKey": "events",
    "mode": "requests",
    "page": 1,
    "perPage": 100,
    "total": 1284,
    "totalPages": 13,
    "columns": ["event_date_time", "page", "status_code"],
    "sort": "event_date_time",
    "sortDir": "desc",
    "period": "7d",
    "window": { "start": "2026-08-16T00:00:00Z", "end": "2026-08-23T23:59:59Z" }
  }
}

A row carries the columns you asked for, plus any companion values those columns pull in (page brings _title and _status_code) and a _rowId. _rowId is an opaque row identifier: the request id on the events explorer, the URL on the Search Console ones. Keys starting with _ are extras: you cannot request them, and passing one in columns is a validation error.

meta echoes the state the query actually ran under, with every default resolved. A request that omits columns, sort, or period still reports what it used. window is the absolute UTC span the rows cover. A query carrying a comparison window also returns compPeriod and compWindow, and the comparison mode returns compCategory.

Export

POST /api/websites/{website}/data-explorer/{explorerKey}/export

An export answers the whole question as a CSV file rather than a page of it. The body is the query body without page and perPage, plus three keys of its own.

bash
curl -X POST https://cloud.edgecomet.com/api/websites/123/data-explorer/events/export \
  -H "Authorization: Bearer eck_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "period": "2026-06-01,2026-06-30",
    "columns": ["event_date_time", "page", "status_code"],
    "filters": [
      { "field": "status_code", "op": "eq", "val": "404" }
    ],
    "filename": "june-404s",
    "maxRows": 50000,
    "waitSeconds": 20
  }'
FieldTypeNotes
filenamestringOptional. Base name for the file, up to 200 characters. .csv is appended.
maxRowsintegerOptional. Row ceiling, up to the exportRowCap discovery reports. Defaults to that cap. A value above it is rejected rather than clamped.
waitSecondsintegerOptional. How long the call waits for the file before answering, 0 to 30. Default 20.

The window is frozen when the export is created. The file covers the span you were told about even though the worker runs later.

While it runs

json
{
  "status": "running",
  "exportId": 4417,
  "message": "Still generating. Poll GET /api/websites/123/data-explorer/export/4417/status, or call export again with the same body to resume waiting."
}

Sending the same body again rejoins the same export instead of starting a second one, whether it is still running or has already finished. A body that differs in any way that changes the question, a different maxRows included, is a different export.

When it finishes

json
{
  "status": "completed",
  "exportId": 4417,
  "rowCount": 8213,
  "sizeBytes": 1048576,
  "columns": ["event_date_time", "page", "status_code"],
  "maxRows": 50000,
  "filename": "june-404s.csv",
  "window": { "start": "2026-06-01", "end": "2026-06-30" },
  "downloadUrl": "https://cloud.edgecomet.com/exports/4417/download/signed?...",
  "expiresAt": "2026-08-23T15:12:00+00:00"
}

rowCount is what the file actually holds and maxRows is the ceiling that was applied: the two being equal is the signal that the file may be truncated. downloadUrl is a signed link that needs no token of its own and expires at expiresAt; a fresh one is minted on every read.

One export runs at a time per account, across the dashboard, the MCP connector, and this API. A second one returns 409 EXPORT_SLOT_BUSY naming the export that holds the slot.

Export status

GET /api/websites/{website}/data-explorer/export/{exportId}/status

bash
curl https://cloud.edgecomet.com/api/websites/123/data-explorer/export/4417/status \
  -H "Authorization: Bearer eck_xxxxxxxxxxxxxxxx"

Returns the same shape as the export call, without waiting. It reads exports started by the token's own user on this website, including ones started from the dashboard or the MCP connector. Anything else returns 404 EXPORT_NOT_FOUND.

A failed export reports why:

json
{
  "status": "failed",
  "exportId": 4417,
  "error": "the export did not finish: The query failed on the analytics database. Try a narrower filter or a smaller row limit.",
  "errorMessage": "The query failed on the analytics database. Try a narrower filter or a smaller row limit."
}

Limits

LimitValue
Rows per query page500
Default rows per query page100
Period range365 days
Rows per export1,000,000
In-call export wait30 seconds
Concurrent exports1 per account

Discovery reports perPageMax, perPageDefault, and exportRowCap for the account making the call.

Failure cases are listed in errors and rate limits.