Assets
An asset is a named, versioned piece of logic that runs against one of your connections and returns a result on demand. There are two types:
- Calculation — runs SQL and returns computed values (a metric, an aggregate, a table, a prediction).
- Visualization — renders a chart, configured by a Plotly
viz_spec. → Visualization Spec Reference
Assets return an insight — a value, a table, or a chart. If you want to serve raw rows of data instead (a queryable data feed), create an Endpoint rather than an asset.
All asset management happens on the Management API (https://api.spartera.com) under your company, authenticated with an analytics key (sk_spartera_...) in the x-api-key header.
https://api.spartera.com/companies/{company_id}/assets
Every code block on this page assumes
BASE_URL,COMPANY_ID,USER_ID, and aHEADERSdict with yourx-api-key. See Python Examples for the full setup and runnable helpers, and the Quickstart Guide for an end-to-end walkthrough.
The Asset Object
Commonly returned fields (exact set varies by asset type and state):
| Field | Description |
|---|---|
asset_id | Unique identifier |
name | Display name |
description | What the asset returns and the question it answers |
asset_type | CALCULATION or VISUALIZATION |
connection_id | The connection the asset runs against |
sql_logic | The SQL query (calculation assets) |
source_schema_name, source_table_name | Source table (visualization assets) |
viz_spec | Plotly figure JSON (visualization assets) |
viz_data_limit | Row cap for visualization rendering (0 = platform max of 10,000) |
tags | List of tag strings |
sell_in_marketplace | Whether the asset is published |
date_created | Creation timestamp |
List Assets
GET /companies/{company_id}/assetsQuery Parameters
page(default 1),per_page(default 20)sort_field(e.g.date_created),sort_direction(asc/desc)asset_type(optional):CALCULATIONorVISUALIZATIONmarketplace(optional):trueto list only published assets
params = {"page": 1, "per_page": 20, "sort_field": "date_created",
"sort_direction": "desc", "asset_type": "VISUALIZATION"}
r = requests.get(f"{BASE_URL}/companies/{COMPANY_ID}/assets",
headers=HEADERS, params=params)
result = r.json()
print(f"Page {result['page']} of {result['total_pages']} ({result['total']} total)")
for asset in result["data"]:
print(f" {asset['asset_id']} — {asset['name']} ({asset['asset_type']})")Create a Calculation Asset
POST /companies/{company_id}/assetsRequest Body
{
"company_id": "{company_id}",
"user_id": "USER_ID",
"name": "90-Day Customer Churn Rate",
"description": "Churn rate as a percentage for the rolling 90-day window.",
"connection_id": "{connection_id}",
"asset_type": "CALCULATION",
"sql_logic": "SELECT ROUND(COUNT(CASE WHEN status='churned' THEN 1 END)*100.0/NULLIF(COUNT(*),0),2) AS churn_rate_pct FROM customers WHERE created_at >= DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY)",
"tags": ["churn", "retention", "daily"]
}industry_id is an optional classification. SQL must return an aggregated/computed result.
Response
{
"data": {
"asset_id": "asset_abc123",
"name": "90-Day Customer Churn Rate",
"asset_type": "CALCULATION",
"sell_in_marketplace": false,
"date_created": "2026-04-01T13:00:00Z"
}
}A newly created asset is a draft (sell_in_marketplace: false) until you publish it.
Create a Visualization Asset
Visualization assets reference a source table (not custom SQL). The columns named by the *src keys in your viz_spec must exist in that table; Spartera generates the query and hydrates the chart at render time.
POST /companies/{company_id}/assetsRequest Body
{
"company_id": "{company_id}",
"user_id": "USER_ID",
"name": "Top 10 Products by Revenue",
"description": "Bar chart of the top revenue-generating products this month.",
"connection_id": "{connection_id}",
"asset_type": "VISUALIZATION",
"source_schema_name": "sales",
"source_table_name": "product_summary",
"viz_spec": {
"data": [
{ "type": "bar", "xsrc": "product_name", "ysrc": "total_revenue_usd", "orientation": "v" }
],
"layout": { "title": "Top Products by Revenue" }
},
"viz_data_limit": 0,
"tags": ["products", "revenue", "bar-chart"]
}| Field | Notes |
|---|---|
source_schema_name / source_table_name | The table the *src keys reference |
viz_spec | Plotly figure JSON. Use *src keys (xsrc, ysrc, labelssrc, valuessrc) to reference columns |
viz_data_limit | 1–10,000; 0 uses the platform max of 10,000 |
The legacy
viz_chart_type/viz_dep_var_col_name/viz_indep_var_col_namecolumns still exist on the record for read access but are not used for rendering — onlyviz_specdrives the chart. Plot title and axis labels are backfilled from the asset name and charted columns if you leave them blank. → Visualization Spec Reference
Get an Asset
GET /companies/{company_id}/assets/{asset_id}The asset is returned as the first element of the data array:
asset = requests.get(f"{BASE_URL}/companies/{COMPANY_ID}/assets/{asset_id}",
headers=HEADERS).json()["data"][0]
print(asset["name"], asset["asset_type"], asset.get("sell_in_marketplace"))Update an Asset
PATCH /companies/{company_id}/assets/{asset_id}Send company_id and user_id plus only the fields you want to change.
def update_asset(asset_id, **fields):
r = requests.patch(f"{BASE_URL}/companies/{COMPANY_ID}/assets/{asset_id}",
headers=HEADERS,
json={"company_id": COMPANY_ID, "user_id": USER_ID, **fields})
return r.json()["data"] if r.status_code == 200 else None
update_asset(asset_id, description="Updated with Q2 2026 coverage")
update_asset(asset_id, rate_limit_number=100,
rate_limit_period="HOUR",
rate_limit_granularity="USER")Delete an Asset
DELETE /companies/{company_id}/assets/{asset_id}requests.delete(f"{BASE_URL}/companies/{COMPANY_ID}/assets/{asset_id}", headers=HEADERS)Test / Preview an Asset
Run a preview against 10% of your data to validate output before publishing. No credits are consumed.
GET /companies/{company_id}/assets/{asset_id}/testFor a calculation, data holds the computed result. For a visualization, the rendered chart's signed image URL is at data.asset_value.
test = requests.get(f"{BASE_URL}/companies/{COMPANY_ID}/assets/{asset_id}/test",
headers=HEADERS).json()
print(test["data"]) # calculation result
print(test["data"].get("asset_value")) # visualization chart URLSave Schema (Dynamic Parameters)
For calculation assets, save the schema after creation to enable buyer-supplied filters/parameters at execution time.
GET /companies/{company_id}/assets/{asset_id}/infoschema/saverequests.get(f"{BASE_URL}/companies/{COMPANY_ID}/assets/{asset_id}/infoschema/save",
headers=HEADERS)Pricing
Set a Price
POST /companies/{company_id}/assets/{asset_id}/pricesrequests.post(f"{BASE_URL}/companies/{COMPANY_ID}/assets/{asset_id}/prices",
headers=HEADERS, json={"price_usd": 3.00})The response includes price_credits (the buyer-facing credit cost). You earn 80% of price_usd per execution. Price changes take effect immediately for new executions.
Get Price History
GET /companies/{company_id}/assets/{asset_id}/prices?active=allReturns all price records with price_usd, price_credits, active, and date_created.
Publish to the Marketplace
Publishing requires Stripe to be connected for your company (→ Quickstart Guide, Step 5). Set sell_in_marketplace along with the two required marketplace metadata fields.
PATCH /companies/{company_id}/assets/{asset_id}requests.patch(f"{BASE_URL}/companies/{COMPANY_ID}/assets/{asset_id}",
headers=HEADERS,
json={
"sell_in_marketplace": True,
"geographic_coverage_type": "GLOBAL",
"data_source_refresh_frequency": "DAILY"
})| Field | Allowed values |
|---|---|
geographic_coverage_type | GLOBAL, CONTINENTAL, REGIONAL, NATIONAL, STATE, LOCAL, CUSTOM, UNKNOWN |
data_source_refresh_frequency | REAL_TIME, HOURLY, DAILY, WEEKLY, MONTHLY, QUARTERLY, ANNUAL, ONE_TIME, CUSTOM, UNKNOWN |
To unpublish, set sell_in_marketplace back to False.
Execute an Asset
Two ways to run an asset:
- Preview during setup —
GET .../test(free, 10% sample; see Test / Preview an Asset above). - Execute for real —
POST .../process(consumes credits at the asset's current price).
POST /companies/{company_id}/assets/{asset_id}/processA body is optional. For parameterized assets (those with a saved schema), pass values under parameters:
r = requests.post(f"{BASE_URL}/companies/{COMPANY_ID}/assets/{asset_id}/process",
headers=HEADERS,
json={"parameters": {"customer_segment": "enterprise"}})
result = r.json()
print(result["data"]) # the computed result
print(result["meta"]["execution_time_ms"])
print(result["meta"]["credits_used"])Response
{
"data": { "churn_rate_pct": 4.2 },
"meta": { "execution_time_ms": 312, "credits_used": 5, "asset_version": "1.0.0" }
}You are never charged credits for a non-200 response.
HTTP Status Codes
| Code | Meaning |
|---|---|
200 OK | Success |
400 Bad Request | Invalid parameters |
401 Unauthorized | Missing or invalid API key |
403 Forbidden | Valid key, insufficient role/permissions |
404 Not Found | Asset doesn't exist |
422 Unprocessable Entity | Valid request, business-logic error (e.g. invalid SQL or spec) |
429 Too Many Requests | Rate limit exceeded |
Related Pages
- Quickstart Guide — End-to-end seller walkthrough
- Python Examples — Runnable helpers for every operation here
- Visualization Spec Reference — The
viz_specschema - Endpoints — The other product type: raw-data feeds
- Connections — Connection types and credentials
- API Keys — Analytics keys, roles, and rotation
- API Overview — Base URLs, auth, and the full API surface
