# query_report_data (https://sellersheetai.com/docs/mcp-api/tools/query_report_data)

> Query the synced report warehouse (rpt_* tables) — rows without calling Amazon.

Access: **READ** · toolset sellersheet · family null

Table and column names: the report-data skill, or get_table_schema(table); a wrong column is a 400 naming the valid ones. `tables` takes the physical name (rpt_get_merchant_listings_all_data), an alias (rpt_listings) or the report-type name (GET_MERCHANT_LISTINGS_ALL_DATA, spCampaigns); data.resolved_tables says what was rewritten.

## Call it

MCP tool call:

```json
{
  "name": "query_report_data",
  "arguments": {
    "store": "myStore-US",
    "tables": [],
    "columns": []
  }
}
```

Tool API (early access — contact us):

```bash
curl -X POST https://api.sellersheetai.com/api/v1/tools/query_report_data \
  -H "Authorization: Bearer $SELLERSHEET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"store":"myStore-US","tables":[],"columns":[]}'
```

## Arguments

| Name | Type | Required | Description |
|---|---|---|---|
| `store` | string | yes | the store ref `<store>-<CC>` (name-country, e.g. 'myStore-US') — one of get_user_context's store_refs, verbatim. rpt_noon_* tables: the noon ref `<account>-<CC>` (e.g. 'myNoon-AE'), never a bare name. |
| `tables` | object[] | yes | table refs; the first is the primary table. |
| `columns` | object[] | yes | 'table.column' strings; '*' = every column of the primary, 'tbl.*' of tbl. Ignored when aggregations is set. |
| `filters` | object[] |  | [{column: 'table.col', op, value}]. Allowed op: eq, neq, gt, lt, gte, lte, contains (case-insensitive), in (a list), not_null, is_null (no value). |
| `joins` | object[] |  | [{table: 'rpt_x', on: {primary_col: joined_col}}] — LEFT JOIN on bare column names. |
| `order_by` | object[] |  | [{column: 'table.col' \| aggregation alias, dir: 'asc'\|'desc'}]. DESC sorts NULLs last. Required whenever offset > 0, or pages overlap. |
| `limit` | integer |  | rows per page, 1–1000 (default 100). Every row enters your context. |
| `offset` | integer |  | pass data.next_offset from the previous page. |
| `report_date` | string |  | 'latest' \| 'all' \| 'YYYY-MM-DD'. Omitted → 'latest' (each marketplace's newest snapshot; data.stale_partitions flags a lagging one) on snapshot/roster tables, 'all' on daily-fact and ledger tables (data.report_date_note says which). A date-column filter makes 'latest' act as 'all'. Unbounded 'all' stops at offset 20000: add a date or eq/in filter, or aggregate. |
| `aggregations` | object[] |  | [{column: 'table.col', op: 'sum'\|'count'\|'avg'\|'min'\|'max', alias}] — the key is op; sort groups with order_by on the alias. |
| `group_by` | object[] |  | ['table.col', 'rpt_orders.purchase_date:month'] — ':day\|week\|month\|quarter\|year' buckets a date; total_count is then the group count. |
| `having` | object[] |  | [{column: '<aggregation alias>', op, value}]; op: eq, neq, gt, lt, gte, lte, in. |

## Result

- data.rows, data.columns: the page; columns are 'table.column' under the physical name.
- `data.total_count` — the full match count, on page 1 only (null later). data.truncated, next_offset, page, total_pages: paging. data.report_date, data.report_date_span, data.stale_partitions, data.report_date_note.
- `data.time_zone` — the times' zone.

## Gotchas

- Ads (rpt_sp_* / rpt_sb_* / rpt_sd_*) and Data Kiosk (rpt_dk_*) tables are one row per entity per DAY: an explicit 'latest' is one scalar date there, often a partial day — add a date filter for any range or ranking; only campaigns that delivered on a day appear, so for a campaign inventory use ads_query_campaign. rpt_get_fba_inventory_planning_data.days_of_supply is NULL for SKUs with no recent sales — filter days_of_supply > 0 for a lowest-cover ranking. Times and days are the store ref's marketplace's ('YYYY-MM-DD HH:MM:SS'). PII blocked.