The /v1/meta endpoint is the foundation of working with the Reporting API. It returns a complete description of the available data model, including:
- Cubes: Logical groupings of related metrics
- Measures: Numeric aggregations you can calculate
- Dimensions: Attributes for grouping and filtering
- Segments: Predefined filters for common business logic
Understanding the metadata structure is essential for building dynamic, resilient clients.
The /v1/meta endpoint returns two types of queryable entities: views and cubes. Each entry has a type field ("view" or "cube") that tells you which it is.
Views are curated, pre-joined interfaces designed for specific reporting use cases. They combine data from multiple cubes, pre-filter where appropriate, and expose human-readable dimensions.
For example, the Sales view:
- Pre-filters to closed (fully settled) orders — no need to add
segments: ["Orders.closed_checks"] - Includes
location_nameinstead of justlocation_id - Has intuitive segments like
Sales.onlineandSales.in_store - Combines order, location, channel, and customer data in one queryable entity
Use views for most reporting tasks. They're simpler to query and less error-prone.
Cubes are the underlying data building blocks. Each cube maps to a specific data domain (orders, items, payments, locations) and exposes its raw measures and dimensions. Cubes are useful when:
- You need a field that isn't exposed in any view
- You need raw, unfiltered data (e.g., including open or voided orders)
- You're doing advanced analysis that doesn't fit a view's pre-defined shape
Warning
Some internal cubes are not intended for direct use. For example, ClosedOrders is an internal cube that backs the Sales view — its description says "Do not use directly — use Sales view instead." Always check a cube's description before querying it directly.
# List all views curl -s -H "Authorization: Bearer $TOKEN" \ https://connect.squareup.com/reporting/v1/meta | \ jq '.cubes[] | select(.type == "view") | {name, description}' # List all cubes curl -s -H "Authorization: Bearer $TOKEN" \ https://connect.squareup.com/reporting/v1/meta | \ jq '.cubes[] | select(.type == "cube") | {name, description}'
curl -X GET "https://connect.squareup.com/reporting/v1/meta" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json"
The metadata response is a JSON object with the following top-level structure:
{ "cubes": [ { "name": "Orders", "title": "Orders", "measures": [...], "dimensions": [...], "segments": [...] } ] }
A cube represents a logical data model containing related measures and dimensions. Each cube corresponds to a business domain (e.g., Orders, Payments, Customers).
{ "name": "Orders", "title": "Orders", "description": "Sales order data including payments, items, and fulfillment", "measures": [...], "dimensions": [...], "segments": [...] }
| Property_Name | Description |
|---|---|
name | Unique identifier for the cube (used in queries) |
title | Human-readable display name |
description | Explanation of what data the cube contains |
measures | Array of available numeric aggregations |
dimensions | Array of available grouping/filtering attributes |
segments | Array of predefined filters |
| View | Purpose | Data Freshness |
|---|---|---|
Sales | Order-level sales, revenue, tips, refunds — pre-filtered to closed orders | ~15 minutes |
ItemSales | Item-level sales with discounts, modifiers, and category data | ~15 minutes |
ModifierSales | Modifier-level sales with modifier list metadata | ~15 minutes |
SpeedOfService | Speed of service metrics and sales data | ~15 minutes |
KDS | Kitchen Display System ticket and item performance | ~15 minutes |
| Cube | Purpose | Data Freshness |
|---|---|---|
Orders | Raw order data including open/voided orders | ~15 minutes |
ItemTransactions | Raw item transaction data | ~15 minutes |
PaymentAndRefunds | Payment and refund transaction details | ~15 minutes |
Location | Location reference data (name, timezone, status) | N/A (reference) |
Note
Use the /v1/meta endpoint or the Schema Explorer for the complete, up-to-date list of available views and cubes.
Measures are numeric values that can be aggregated (summed, averaged, counted, etc.). They represent the "what you're measuring" in your query.
{ "name": "Orders.net_sales", "title": "Net Sales", "description": "Total sales after discounts and returns, excluding tax", "type": "number", "aggType": "sum", "drillMembers": ["Orders.order_id", "Orders.sale_timestamp"], "format": "currency" }
| Property_Name | Description |
|---|---|
name | Fully qualified measure name (Cube.measure) |
title | Human-readable display name |
description | Detailed explanation of what the measure calculates |
type | Data type (number, string, time) |
aggType | Aggregation type (sum, count, avg, min, max) |
drillMembers | Suggested dimensions for drill-down analysis |
format | Display format hint (currency, percent, number) |
From the Orders cube:
| Measure | Description | Use Case |
|---|---|---|
Orders.net_sales | Gross sales minus discounts/returns | Primary revenue metric |
Orders.net_sales_with_tax | Net sales plus sales tax | Gross revenue in tax-inclusive regions |
Orders.top_line_product_sales | Gross product sales before adjustments | Starting point for sales funnel |
Orders.discounts_amount | Total discounts applied | Promotional effectiveness |
Orders.itemized_returns | Value of returned items | Return rate analysis |
Orders.sales_tax_amount | Sales tax collected | Tax reporting |
Orders.tips_amount | Tips received (non-cash) | Service performance |
Orders.comps_amount | Complimentary items value | Comp tracking |
Most monetary measures use sum aggregation:
{ "name": "Orders.net_sales", "aggType": "sum" }
For counting distinct entities:
{ "name": "Orders.count", "aggType": "count" }
For calculating averages:
{ "name": "Orders.avg_net_sales", "aggType": "avg" }
Dimensions are attributes used to group, filter, or slice your data. They represent the "how you're slicing" your measures.
{ "name": "Orders.location_id", "title": "Location ID", "description": "Unique identifier for the location where the order was placed", "type": "string", "suggestFilterValues": true }
| Property_Name | Description |
|---|---|
name | Fully qualified dimension name (Cube.dimension) |
title | Human-readable display name |
description | Explanation of what the dimension represents |
type | Data type (string, number, time) |
suggestFilterValues | Whether the API can suggest filter values |
String-based attributes for grouping:
{ "name": "Orders.location_id", "type": "string" }
Common categorical dimensions:
Orders.location_id— Group by locationOrders.merchant_id— Group by merchantOrders.sales_channel_id— Group by sales channel (online, in-person, etc.)Orders.device_id— Group by device
Special dimensions for time-series analysis:
{ "name": "Orders.sale_timestamp", "type": "time" }
Time dimensions support granularity:
day— Daily aggregationweek— Weekly aggregationmonth— Monthly aggregationquarter— Quarterly aggregationyear— Yearly aggregation
For business-day alignment:
{ "name": "Orders.local_date", "type": "time" }
Use local time dimensions when you need results aligned to the location's timezone rather than UTC.
| Dimension | Type | Description |
|---|---|---|
Orders.sale_timestamp | time | UTC timestamp of sale |
Orders.local_date | time | Local date in location timezone |
Orders.location_id | string | Location identifier |
Orders.merchant_id | string | Merchant identifier |
Orders.sales_channel_id | string | Sales channel (e.g., online, in-person) |
Orders.device_id | string | Device used for transaction |
Orders.customer_id | string | Customer identifier |
Segments are predefined filters that encapsulate common business logic. They ensure consistency across queries and simplify complex filtering.
{ "name": "Orders.closed_checks", "title": "Closed Checks", "description": "Filters to fully paid and settled orders, matching Sales Summary behavior" }
| Property_Name | Description |
|---|---|
name | Fully qualified segment name (Cube.segment) |
title | Human-readable display name |
description | Explanation of the filter logic |
| Segment | Purpose |
|---|---|
Orders.closed_checks | Fully paid/settled orders (recommended for Sales Summary parity) |
Orders.open_checks | Orders awaiting payment |
Orders.awaiting_capture | Orders awaiting payment capture |
Orders.fully_paid | Fully paid orders |
Orders.has_tip | Orders that include a tip |
Orders.no_tip | Orders without a tip |
Segments provide several benefits:
- Consistency: Ensures everyone uses the same filter logic
- Simplicity: Encapsulates complex conditions in a single reference
- Maintainability: Filter logic can be updated centrally
- Report Parity: Matches Square dashboard behavior
Example: Instead of manually filtering by order state:
{ "filters": [{ "member": "Orders.state", "operator": "equals", "values": ["COMPLETED"] }, { "member": "Orders.check_state", "operator": "equals", "values": ["CLOSED"] }] }
Simply use the segment:
{ "segments": ["Orders.closed_checks"] }
Cubes, measures, dimensions, and segments can include a meta field with additional metadata tags. These tags provide UI hints, stability information, and semantic context beyond the basic properties.
| Tag | Applies To | Purpose |
|---|---|---|
label | Dimensions, Measures, Segments | Human-readable display label (e.g., "Net sales") |
tooltip | Dimensions, Measures, Segments | Short tooltip text for UI hover states |
context | Dimensions, Measures, Segments | Extended context for LLMs and advanced UIs |
stability | Cubes (required), Dimensions, Measures | Data quality and API stability level |
deprecated_at | Cubes, Dimensions, Measures | Deprecation date (ISO 8601, when stability is deprecated) |
values | Dimensions, Segments | Allowed values for enum-like fields |
translation_types | Dimensions | Values requiring localization |
examples | Cubes | Example queries demonstrating cube usage |
The stability tag communicates the maturity and reliability of a cube or field:
| Value | Data Quality | Breaking Changes | Description |
|---|---|---|---|
preview | Uncertain (may have missing data) | Highly likely | Early access, under active development |
beta | Uncertain (often missing historical data) | Unlikely but possible | Approaching general availability |
ga | Verified | None | General Availability — production-ready |
deprecated | N/A | N/A | Supported for 12+ months, then removed |
Stability is set at the cube level and can be overridden at the field level. For example, a ga cube might have an individual measure marked as preview.
{ "name": "Orders.net_sales", "title": "Net Sales", "type": "number", "aggType": "sum", "format": "currency", "meta": { "label": "Net sales", "tooltip": "Amount of gross sales minus discounts, comps, returns, refunds, tips, taxes, fees, or gift card sales.", "stability": "beta" } }
{ "name": "Orders.check_state", "title": "Check State", "type": "string", "meta": { "label": "Check state", "tooltip": "The payment status of the order.", "values": ["AWAITING_CAPTURE_CHECK", "CLOSED_CHECK", "OPEN_CHECK"] } }
For the complete meta tags reference with detailed examples and best practices, see the Meta Tags Reference.
import requests import os def get_cube_measures(cube_name): """Fetch and parse measures from a specific cube.""" response = requests.get( 'https://connect.squareup.com/reporting/v1/meta', headers={'Authorization': f'Bearer {os.environ["SQUARE_ACCESS_TOKEN"]}'} ) metadata = response.json() # Find the cube cube = next((c for c in metadata['cubes'] if c['name'] == cube_name), None) if not cube: raise ValueError(f"Cube {cube_name} not found") # Extract measure information measures = [] for measure in cube['measures']: measures.append({ 'name': measure['name'], 'title': measure['title'], 'description': measure.get('description', ''), 'type': measure['type'], 'aggType': measure.get('aggType', 'unknown') }) return measures # Usage measures = get_cube_measures('Orders') for measure in measures: print(f"{measure['name']}: {measure['title']}")
async function buildDimensionSelector(cubeName) { const response = await fetch('https://connect.squareup.com/reporting/v1/meta', { headers: { 'Authorization': `Bearer ${process.env.SQUARE_ACCESS_TOKEN}` } }); const metadata = await response.json(); const cube = metadata.cubes.find(c => c.name === cubeName); if (!cube) { throw new Error(`Cube ${cubeName} not found`); } // Build selector options return cube.dimensions.map(dimension => ({ value: dimension.name, label: dimension.title, description: dimension.description, type: dimension.type })); } // Usage const dimensions = await buildDimensionSelector('Orders'); console.log(dimensions);
def validate_query(query, metadata): """Validate that a query uses valid measures, dimensions, and segments.""" cube_name = query.get('cube', 'Orders') cube = next((c for c in metadata['cubes'] if c['name'] == cube_name), None) if not cube: return False, f"Cube {cube_name} not found" # Validate measures valid_measures = {m['name'] for m in cube['measures']} for measure in query.get('measures', []): if measure not in valid_measures: return False, f"Invalid measure: {measure}" # Validate dimensions valid_dimensions = {d['name'] for d in cube['dimensions']} for dimension in query.get('dimensions', []): if dimension not in valid_dimensions: return False, f"Invalid dimension: {dimension}" # Validate segments valid_segments = {s['name'] for s in cube['segments']} for segment in query.get('segments', []): if segment not in valid_segments: return False, f"Invalid segment: {segment}" return True, "Query is valid" # Usage query = { "measures": ["Orders.net_sales"], "dimensions": ["Orders.location_id"], "segments": ["Orders.closed_checks"] } is_valid, message = validate_query(query, metadata) print(f"Valid: {is_valid}, Message: {message}")
Metadata changes infrequently, so caching is recommended:
import time class MetadataCache: def __init__(self, ttl_seconds=3600): self.cache = None self.last_fetch = 0 self.ttl = ttl_seconds def get(self): now = time.time() if self.cache is None or (now - self.last_fetch) > self.ttl: self.cache = self._fetch_metadata() self.last_fetch = now return self.cache def _fetch_metadata(self): response = requests.get( 'https://connect.squareup.com/reporting/v1/meta', headers={'Authorization': f'Bearer {os.environ["SQUARE_ACCESS_TOKEN"]}'} ) return response.json() def invalidate(self): self.cache = None # Usage cache = MetadataCache(ttl_seconds=3600) # 1 hour TTL metadata = cache.get()
- Recommended TTL: 1-24 hours depending on your needs
- Invalidation: Clear cache after query errors related to schema
- Startup: Always fetch fresh metadata on application boot
Don't hard-code measure/dimension lists. Build them from metadata:
// Good: Dynamic const measures = metadata.cubes.Orders.measures.map(m => ({ label: m.title, value: m.name })); // Bad: Hard-coded const measures = [ { label: 'Net Sales', value: 'Orders.net_sales' }, { label: 'Tips', value: 'Orders.tips_amount' } ];
Check that your query components exist before sending to /v1/load:
# Validate first is_valid, error = validate_query(query, metadata) if not is_valid: raise ValueError(error) # Then execute results = execute_query(query)
Always use Orders.closed_checks when building sales reports that should match the Square dashboard:
{ "measures": ["Orders.net_sales"], "segments": ["Orders.closed_checks"] }
Track which measures and dimensions your application requires:
# app-requirements.yml required_measures: - Orders.net_sales - Orders.tips_amount required_dimensions: - Orders.location_id - Orders.sale_timestamp required_segments: - Orders.closed_checks
Use jq to explore metadata from the command line:
curl -s -H "Authorization: Bearer $TOKEN" \ https://connect.squareup.com/reporting/v1/meta | \ jq '.cubes[] | select(.type == "view") | .name'
curl -s -H "Authorization: Bearer $TOKEN" \ https://connect.squareup.com/reporting/v1/meta | \ jq '.cubes[].name'
curl -s -H "Authorization: Bearer $TOKEN" \ https://connect.squareup.com/reporting/v1/meta | \ jq '.cubes[] | select(.name == "Orders") | .measures[] | {name, title, description}'
curl -s -H "Authorization: Bearer $TOKEN" \ https://connect.squareup.com/reporting/v1/meta | \ jq '.cubes[] | select(.name == "Orders") | .dimensions[] | select(.type == "time") | .name'
curl -s -H "Authorization: Bearer $TOKEN" \ https://connect.squareup.com/reporting/v1/meta | \ jq '.cubes[] | select(.name == "Orders") | .segments[] | {name, title}'