Analytics Endpoints

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 a HEADERS dict with your x-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):

FieldDescription
asset_idUnique identifier
nameDisplay name
descriptionWhat the asset returns and the question it answers
asset_typeCALCULATION or VISUALIZATION
connection_idThe connection the asset runs against
sql_logicThe SQL query (calculation assets)
source_schema_name, source_table_nameSource table (visualization assets)
viz_specPlotly figure JSON (visualization assets)
viz_data_limitRow cap for visualization rendering (0 = platform max of 10,000)
tagsList of tag strings
sell_in_marketplaceWhether the asset is published
date_createdCreation timestamp

List Assets

GET /companies/{company_id}/assets

Query Parameters

  • page (default 1), per_page (default 20)
  • sort_field (e.g. date_created), sort_direction (asc / desc)
  • asset_type (optional): CALCULATION or VISUALIZATION
  • marketplace (optional): true to 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}/assets

Request 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}/assets

Request 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"]
}
FieldNotes
source_schema_name / source_table_nameThe table the *src keys reference
viz_specPlotly figure JSON. Use *src keys (xsrc, ysrc, labelssrc, valuessrc) to reference columns
viz_data_limit1–10,000; 0 uses the platform max of 10,000

The legacy viz_chart_type / viz_dep_var_col_name / viz_indep_var_col_name columns still exist on the record for read access but are not used for rendering — only viz_spec drives 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}/test

For 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 URL

Save 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/save
requests.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}/prices
requests.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=all

Returns 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"
               })
FieldAllowed values
geographic_coverage_typeGLOBAL, CONTINENTAL, REGIONAL, NATIONAL, STATE, LOCAL, CUSTOM, UNKNOWN
data_source_refresh_frequencyREAL_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}/process

A 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

CodeMeaning
200 OKSuccess
400 Bad RequestInvalid parameters
401 UnauthorizedMissing or invalid API key
403 ForbiddenValid key, insufficient role/permissions
404 Not FoundAsset doesn't exist
422 Unprocessable EntityValid request, business-logic error (e.g. invalid SQL or spec)
429 Too Many RequestsRate limit exceeded

Related Pages