Quickstart Guide
This guide takes you end to end as a seller: get an API key, connect a data source, build a calculation asset and a visualization asset, then (when you're ready) connect Stripe and publish to the marketplace.
You'll build your products first and save them as drafts — nothing goes live or becomes purchasable until you explicitly publish in the final section. That means you can complete Steps 1–4 today without any billing setup.
In a hurry? The fastest path to one live product is: get a key (Step 1) → connect a source (Step 3) → create one calculation asset (Step 4a) → connect Stripe and publish (Step 5). The visualization and endpoint sections are additive.
All examples use Python with the requests library and call the REST API directly — no SDK required. → See Python Examples for the full function library this guide is built on.
Prerequisites
- A Spartera account (sign up free)
- Python 3.7+ and
requests(pip install requests) - Access to a supported data source — BigQuery, Snowflake, Redshift, and more (→ Data Sources)
Step 1: Get Your API Key
Your first key has to be created in the dashboard (you need a key to mint more keys via the API, so the first one is a manual step).
Dashboard → Settings → API Keys → Generate New Key
When you create it, you assign the key a role. The key can do anything that role can do, but no more. Because this guide creates a connection, you need at least a Data Engineer (role 3) key — a read-only Analyst (role 1) key will fail at Step 3.
| Role | Can it complete this guide? |
|---|---|
| 1 — Analyst (read-only) | ❌ Can't create connections or assets |
| 2 — Data Scientist | ⚠️ Can create assets, but not connections |
| 3 — Data Engineer | ✅ Connections + assets + endpoints |
| 4 — Admin | ✅ Everything |
You'll get an analytics key that starts with sk_spartera_. Copy it immediately — the full value is shown only once. After you close the dialog, only a masked version is retrievable.
→ Full key model, roles, and rotation: API Keys.
Step 2: Set Up Your Script and Confirm Your IDs
Almost every call needs three values: your API key, your company_id, and your user_id. You can look up the last two from your key with GET /users/me.
import requests
import json
BASE_URL = "https://api.spartera.com"
API_KEY = "sk_spartera_..." # the key from Step 1
HEADERS = {
"x-api-key": API_KEY,
"Content-Type": "application/json"
}
# Look up your company_id and user_id
me = requests.get(f"{BASE_URL}/users/me", headers=HEADERS).json()["data"]
COMPANY_ID = me["company"]["company_id"]
USER_ID = me["user_id"]
print(f"Company: {COMPANY_ID}")
print(f"User: USER_ID")Keep COMPANY_ID and USER_ID around — the rest of the guide uses them.
Step 3: Create a Connection
A connection is a read-only link to the database where your data lives. Spartera connects, runs your asset's logic inside your database, and returns only the result — your raw data never leaves your infrastructure.
Credentials differ by engine. The example below uses BigQuery (a service-account JSON); Snowflake uses a username/password block; other engines have their own shapes (→ Connections).
def create_bigquery_connection(name, description, service_account_json, provider_domain):
payload = {
"company_id": COMPANY_ID,
"user_id": USER_ID,
"engine_id": 1, # 1 = BigQuery
"name": name,
"description": description,
"provider_domain": provider_domain, # where the data originates, e.g. "yourcompany.com"
"credential_type": "SERVICE_ACCOUNT",
"credentials": json.dumps(service_account_json),
"verified_usage_ability": True
}
r = requests.post(f"{BASE_URL}/companies/{COMPANY_ID}/connections",
headers=HEADERS, json=payload)
result = r.json()
if r.status_code == 200:
connection_id = result["data"]["connection_id"]
print(f"✅ Connection created: {connection_id}")
return connection_id
print(f"❌ Error: {result}")
return None
# Load your GCP service-account key file and create the connection
with open("service-account-key.json") as f:
sa_key = json.load(f)
CONNECTION_ID = create_bigquery_connection(
name="Production BigQuery — Analytics",
description="Main analytics warehouse, refreshed daily at 2 AM UTC.",
service_account_json=sa_key,
provider_domain="yourcompany.com"
)BigQuery permissions: grant the service account
roles/bigquery.dataViewerandroles/bigquery.jobUser. Spartera only ever reads.
Test the connection before building on it — this catches credential and permission problems early, while they're easy to diagnose:
test = requests.get(
f"{BASE_URL}/companies/{COMPANY_ID}/connections/{CONNECTION_ID}/test",
headers=HEADERS
).json()
print(f"Connection status: {test['data']['test_status']}") # SUCCESS / PARTIAL / FAILEDSave your CONNECTION_ID — every asset references it.
Step 4: Create Your Products
A product on Spartera is either an asset or an endpoint:
- An asset runs your SQL (or visualization) logic on demand and returns the result — a number, a table, or a rendered chart. There are two types: Calculation and Visualization.
- An endpoint serves a rowset data feed that buyers (or their apps and agents) pull from a per-seller URL. → Endpoint API Reference
In this step you'll create one of each asset type and save them as drafts. We do not publish here — that's Step 5.
4a. Create a Calculation Asset
A calculation asset returns computed values: a metric, an aggregate, a prediction.
def create_calculation_asset(name, description, connection_id, sql_logic, tags=None):
payload = {
"company_id": COMPANY_ID,
"user_id": USER_ID,
"name": name,
"description": description,
"connection_id": connection_id,
"asset_type": "CALCULATION",
"sql_logic": sql_logic,
}
if tags:
payload["tags"] = tags
r = requests.post(f"{BASE_URL}/companies/{COMPANY_ID}/assets",
headers=HEADERS, json=payload)
result = r.json()
if r.status_code == 200:
asset_id = result["data"]["asset_id"]
print(f"✅ Calculation asset created: {asset_id}")
return asset_id
print(f"❌ Error: {result}")
return None
calc_asset_id = create_calculation_asset(
name="90-Day Customer Churn Rate",
description=(
"Customer churn rate as a percentage for the rolling 90-day window. "
"Sourced from our production CRM, refreshed daily."
),
connection_id=CONNECTION_ID,
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"]
)Preview it against 10% of your data to confirm the output looks right before anyone can buy it:
test = requests.get(
f"{BASE_URL}/companies/{COMPANY_ID}/assets/{calc_asset_id}/test",
headers=HEADERS
).json()
print(f"Preview result: {test['data']}")(Optional) Save the schema to let buyers pass dynamic filters/parameters at execution time:
requests.get(
f"{BASE_URL}/companies/{COMPANY_ID}/assets/{calc_asset_id}/infoschema/save",
headers=HEADERS
)4b. Create a Visualization Asset
A visualization asset returns a rendered chart. Charts are configured with a single JSON field, viz_spec, which holds a Plotly figure. Instead of inline data, you reference columns from your source table using *src keys (xsrc, ysrc, labelssrc, valuessrc); Spartera hydrates them with live data at render time.
A few things worth knowing before you build a chart:
- The legacy
viz_chart_type/viz_dep_var_col_name/viz_indep_var_col_namefields are no longer used for rendering. Onlyviz_specdrives the chart. (Some older examples still show those fields — ignore them.)- Give a bar chart both an X and a Y. A bar with an X column but no Y makes Plotly fall back to summing X — which looks fine on a small preview and wrong at full data volume. If you want counts per category, aggregate in a view first (
GROUP BY category→ a count column) and chart that count column asysrc.- You don't have to set titles. If you leave the plot title or axis titles blank, Spartera now backfills them from the asset name and the charted columns at render time.
→ Full schema, the
*srcconvention, chart-type tables, and validation rules: Visualization Spec Reference. For more chart shapes (line, area, pie, histogram): Python Examples.
A visualization asset references a source table (not custom SQL) — the columns named in your viz_spec must exist in that table.
def create_visualization_asset(name, description, connection_id,
schema_table, viz_spec, data_limit=0, tags=None):
schema_name, table_name = schema_table.split(".")
payload = {
"company_id": COMPANY_ID,
"user_id": USER_ID,
"name": name,
"description": description,
"connection_id": connection_id,
"asset_type": "VISUALIZATION",
"source_schema_name": schema_name,
"source_table_name": table_name,
"viz_spec": viz_spec,
"viz_data_limit": data_limit, # 0 = platform max (10,000 rows)
}
if tags:
payload["tags"] = tags
r = requests.post(f"{BASE_URL}/companies/{COMPANY_ID}/assets",
headers=HEADERS, json=payload)
result = r.json()
if r.status_code == 200:
asset_id = result["data"]["asset_id"]
print(f"✅ Visualization asset created: {asset_id}")
return asset_id
print(f"❌ Error: {result}")
return None
# A clean bar chart: explicit X and Y, both real columns in the source table.
bar_spec = {
"data": [
{
"type": "bar",
"xsrc": "product_name",
"ysrc": "total_revenue_usd",
"orientation": "v"
}
],
"layout": {
"title": "Top Products by Revenue" # optional — backfilled if omitted
}
}
viz_asset_id = create_visualization_asset(
name="Top 10 Products by Revenue — Current Month",
description="Bar chart of the top revenue-generating products this month.",
connection_id=CONNECTION_ID,
schema_table="sales.product_summary", # already aggregated: one row per product
viz_spec=bar_spec,
tags=["products", "revenue", "bar-chart", "monthly"]
)Render it to confirm it looks right. For a visualization, the /test response returns the rendered chart's signed image URL at data.asset_value:
test = requests.get(
f"{BASE_URL}/companies/{COMPANY_ID}/assets/{viz_asset_id}/test",
headers=HEADERS
).json()
print(f"Chart URL: {test['data'].get('asset_value')}")Open that URL in a browser to see your chart against live data.
Both assets are now saved as drafts. They exist, they're testable, but they are not in the marketplace and nobody can buy them. Publishing happens in Step 5.
4c. Create an Endpoint (Optional)
An endpoint is a different product shape from an asset: instead of returning a single computed result or chart, it serves a rowset data feed that buyers pull (with pagination and filtering) from your per-seller URL:
https://{your_handle}.api.spartera.com/endpoints/v1/{endpoint_slug}
Endpoints have their own concepts that assets don't — an ACTIVE/inactive status, a max_records_per_request cap, and dedicated read-only endpoint keys (ep_...) that are auto-provisioned to buyers on first use. Create and configure an endpoint from the dashboard:
Dashboard → Endpoints → Create Endpoint
→ The customer-facing contract (request, response envelope, pagination, filtering): Endpoint API Reference. How endpoint keys work: API Keys.
Step 5: Monetize Your Products
Everything so far is free and reversible. To actually sell, you connect Stripe once, then publish each product with a price.
5a. Connect Stripe
Spartera pays out seller earnings through Stripe Connect. You connect your Stripe account once, and from then on the platform can route the seller share of each purchase to you automatically. Sellers receive 80% of each transaction; Spartera retains 20% (→ Credit System).
Dashboard → Settings → Payments → Connect Stripe
This opens Stripe's onboarding flow. You can't publish a product until Stripe is connected — the publish call below will be rejected otherwise.
5b. Publish — Set a Price and Add Metadata
Publishing is two pieces: set a price, then flip the asset into the marketplace with the required marketplace metadata.
Set a price (you earn 80% of this per execution):
def set_asset_price(asset_id, price_usd):
r = requests.post(f"{BASE_URL}/companies/{COMPANY_ID}/assets/{asset_id}/prices",
headers=HEADERS, json={"price_usd": price_usd})
result = r.json()
if r.status_code == 200:
print(f"✅ Price set: ${price_usd} → you earn ${price_usd * 0.80:.2f}/call")
return result["data"]
print(f"❌ Error: {result}")
return None
set_asset_price(calc_asset_id, price_usd=3.00)Publish by setting sell_in_marketplace along with the two required marketplace metadata fields:
def publish_asset(asset_id,
geographic_coverage_type="GLOBAL",
data_source_refresh_frequency="DAILY"):
"""
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
"""
payload = {
"sell_in_marketplace": True,
"geographic_coverage_type": geographic_coverage_type,
"data_source_refresh_frequency": data_source_refresh_frequency
}
r = requests.patch(f"{BASE_URL}/companies/{COMPANY_ID}/assets/{asset_id}",
headers=HEADERS, json=payload)
if r.status_code == 200:
print(f"✅ Asset {asset_id} is live in the marketplace")
return r.json()["data"]
print(f"❌ Error: {r.json()}")
return None
publish_asset(calc_asset_id, data_source_refresh_frequency="DAILY")To unpublish at any time, set sell_in_marketplace back to False:
requests.patch(f"{BASE_URL}/companies/{COMPANY_ID}/assets/{calc_asset_id}",
headers=HEADERS, json={"sell_in_marketplace": False})That's it — your product is discoverable and purchasable. → For the full asset lifecycle (update, price history, delete, execute-as-a-buyer): Assets and Python Examples.
Step 6: Getting Help
| Channel | Where |
|---|---|
| [email protected] | |
| Web form | spartera.com/contact |
| Slack | Ask to be invited (email support, or use the web form) |
| Discord | Public — invite link at spartera.com/contact |
| Bugs & ideas | ideafactory.spartera.com |
What to Explore Next
| Goal | Guide |
|---|---|
| The full asset API (CRUD, test, execute, pricing) | Assets |
Every chart type and the viz_spec schema | Visualization Spec Reference |
| Copy-paste Python for every operation | Python Examples |
| Skip writing SQL — let AI build assets | AutoInsights |
| Connect AI agents to your analytics | SparteraConnect |
| Serve rowset data feeds | Endpoint API Reference |
| How pricing and payouts work | Credit System |
| All supported databases | Data Sources |
