API Quick Start

This page walks through common API calls with copy-pasteable examples. Every example assumes you have a Personal API Key.

🧪

The Upside API is in beta. Endpoints and response formats may change. There are no guaranteed SLAs at this time. If you have questions, reach out to your Upside team on Slack.

Replace sk_your_key_here with your actual key in all examples below.


Check that your key works

A quick way to verify your key is valid:

curl:

curl https://app.upside.tech/api/datahub/accounts/?limit=1 \
  -H "Authorization: Bearer sk_your_key_here"

Python:

import requests

API_KEY = "sk_your_key_here"
BASE = "https://app.upside.tech/api"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

r = requests.get(f"{BASE}/datahub/accounts/", params={"limit": 1}, headers=HEADERS)
print(r.status_code)  # 200 if your key works

If you get 200, you're in. If you get 401, double-check your key.


List your accounts

Pull a paginated list of accounts:

curl:

curl "https://app.upside.tech/api/datahub/accounts/?limit=50&offset=0" \
  -H "Authorization: Bearer sk_your_key_here"

Python:

r = requests.get(
    f"{BASE}/datahub/accounts/",
    params={"limit": 50, "offset": 0, "sort_by": "name"},
    headers=HEADERS,
)
data = r.json()

print(f"Showing {len(data['results'])} of {data['total']} accounts")
for account in data["results"]:
    print(account["name"])

Response shape:

{
  "results": [
    {
      "id": "acc_...",
      "name": "Acme Corp",
      "source_record_id": "001..."
    }
  ],
  "total": 342,
  "limit": 50,
  "offset": 0
}

Search opportunities

Search for opportunities by name:

curl:

curl "https://app.upside.tech/api/datahub/opportunities/?search=enterprise&limit=25" \
  -H "Authorization: Bearer sk_your_key_here"

Python:

r = requests.get(
    f"{BASE}/datahub/opportunities/",
    params={"search": "enterprise", "limit": 25},
    headers=HEADERS,
)
data = r.json()

for opp in data["results"]:
    print(f"{opp['name']} — {opp.get('stage', 'N/A')}")

Run a SQL query

Execute a read-only SQL query against your Upside data warehouse. Results stream back as newline-delimited JSON.

curl:

curl -X POST https://app.upside.tech/api/query/snowflake/ \
  -H "Authorization: Bearer sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"query": "SELECT name, stage FROM opportunities WHERE close_date > CURRENT_DATE LIMIT 10"}'

Python:

r = requests.post(
    f"{BASE}/query/snowflake/",
    headers={**HEADERS, "Content-Type": "application/json"},
    json={"query": "SELECT name, stage FROM opportunities WHERE close_date > CURRENT_DATE LIMIT 10"},
    stream=True,
)

for line in r.iter_lines():
    if line:
        print(line.decode())

Response (streamed, one JSON object per line):

{"type":"metadata","columns":["NAME","STAGE"],"column_types":["TEXT","TEXT"],"query_id":"01b..."}
["Acme Corp Expansion","Negotiation"]
["Beta Inc Renewal","Closed Won"]
{"type":"summary","row_count":2,"execution_time_ms":245}

The first line is metadata (column names and types), followed by one array per row, and a summary line at the end.

💡

SQL queries are read-only and have a 30-second timeout. For large queries, add a LIMIT clause or narrow your filters.


List your miniapps

curl:

curl https://app.upside.tech/api/miniapps/ \
  -H "Authorization: Bearer sk_your_key_here"

Python:

r = requests.get(f"{BASE}/miniapps/", headers=HEADERS)
apps = r.json()

for app in apps:
    print(f"{app['name']} — {app.get('status', 'unknown')}")

Pagination

Data endpoints support limit and offset:

all_accounts = []
offset = 0
limit = 100

while True:
    r = requests.get(
        f"{BASE}/datahub/accounts/",
        params={"limit": limit, "offset": offset},
        headers=HEADERS,
    )
    data = r.json()
    all_accounts.extend(data["results"])

    if offset + limit >= data["total"]:
        break
    offset += limit

print(f"Fetched {len(all_accounts)} accounts")

Handling errors

Check the status code before parsing the response:

r = requests.get(f"{BASE}/datahub/accounts/", headers=HEADERS)

if r.status_code == 200:
    data = r.json()
elif r.status_code == 401:
    print("Invalid or expired key. Check your API key.")
elif r.status_code == 429:
    retry_after = r.headers.get("Retry-After", "a few seconds")
    print(f"Rate limited. Retry after {retry_after}.")
else:
    print(f"Error {r.status_code}: {r.text}")

Did this page help you?