Blog/Developer Tools/

X/Twitter Monitoring API: Search to Author | SandBase

Build an X/Twitter monitoring workflow: search a topic, read a tweet's engagement, and profile its author — one SandBase key, no X developer tier, no OAuth.

Dark cinematic render of a search query resolving into tweet cards and an author profile signal feeding an agent core

Monitoring on X (Twitter) comes down to three moves: find tweets about a topic, measure how each one is landing, and understand who posted it. This tutorial wires those three moves into one workflow using the SandBase X/Twitter API — no X developer tier of your own and no OAuth, though you still need a SandBase API key. These are synchronous request/response reads, so “real-time” here means polling on a schedule rather than a streaming subscription. The endpoint reference only guarantees the response envelope (id, status, model, outputs[0].data); the business fields shown below are an illustrative structure, not a guaranteed schema, so treat both the field names and the values as examples and confirm them against a live response.

If you want the full endpoint tour first, start with the X/Twitter public data API hub. This piece is the applied monitoring workflow.

Key takeaway

  • Three steps: search-timeline (discover) → tweet-detail (engagement) → user-profile (author).
  • Each web call is POST /v1/api/twitter/web/<endpoint> with one SANDBASE_API_KEY; responses share the { id, status, model, outputs } envelope.
  • Carry the tweet_id from search into tweet-detail, and the screen_name into user-profile.
  • Only the envelope (id, status, model, outputs[0].data) is guaranteed; the business fields are illustrative — confirm them against a live response.
  • Public, read-only data only; no X developer tier of your own and no posting, but you do need a SandBase API key.

The workflow at a glance

StepEndpointInputYou get
1. Discovertwitter/web/search-timelinekeyword (optional cursor)a timeline of tweets
2. Measuretwitter/web/tweet-detailtweet_idtext, likes, retweets, views
3. Profiletwitter/web/user-profilescreen_namefollower and account signals

SandBase X endpoint reference showing the search, tweet, and profile endpoints used in this workflow The endpoint API reference is the source of truth for each parameter name and response path.

Step 1 — Discover tweets about your topic

Search a keyword in Top or Latest mode. search-timeline accepts an optional cursor request parameter you carry over from the previous request to page:

import os
import requests

BASE = "https://api.sandbase.ai/v1/api/twitter"
HEADERS = {
    "Authorization": f"Bearer {os.environ['SANDBASE_API_KEY']}",
    "Content-Type": "application/json",
}


def call(path: str, payload: dict) -> dict:
    resp = requests.post(f"{BASE}/{path}", headers=HEADERS, json=payload, timeout=90)
    resp.raise_for_status()
    body = resp.json()
    if body.get("status") != "completed":
        raise RuntimeError(body.get("error", {}).get("message", "request did not complete"))
    return body["outputs"][0]["data"]


search = call("web/search-timeline", {"keyword": "NASA", "search_type": "Top"})
# inspect a real response and read the schema to locate tweet ids;
# carry the optional `cursor` request parameter over from the previous
# request to page — do not rely on a specific response field name

Inspect one real response to see exactly where tweet ids sit in the result, since only the envelope is guaranteed and the business layout may differ from any example. Then collect the ids you want to measure. To page, pass the optional cursor request parameter — a value you carry over from the previous request — rather than depending on a fixed field in the response body.

Step 2 — Measure a tweet’s engagement

For each tweet_id, read the full tweet with its engagement counts:

tweet = call("web/tweet-detail", {"tweet_id": "2041690396586090592"})
print((tweet.get("display_text") or "")[:60])
# example output: It's not just a phase 🌕 Artemis II astronauts captured these views...
print(tweet.get("retweets"), "retweets,", tweet.get("replies"), "replies,", tweet.get("views"), "views")
# example output: 17850 retweets, 1173 replies, 3880967 views

In this illustrative shape a tweet read can include fields such as display_text, created_at, lang, engagement counts (likes, retweets, replies, views, quotes, bookmarks), entities, media, and an embedded author. Only the envelope is guaranteed, so the code uses .get() rather than assuming those keys are present, and the printed values are illustrative. That embedded author can save you a call when you only need basic account context. The block below is an illustrative response shape — the business fields are not a guaranteed schema, so treat both the field names and the values as examples and confirm them against a live response, since payloads vary and change over time:

{
  "id": "117db5dd-1189-4830-9459-98b624960286",
  "status": "completed",
  "model": "twitter/web/tweet-detail",
  "outputs": [
    {
      "data": {
        "id": "2041690396586090592",
        "created_at": "Wed Apr 08 01:31:37 +0000 2026",
        "display_text": "It's not just a phase 🌕 ...",
        "retweets": 17850,
        "replies": 1173,
        "views": 3880967,
        "author": { "name": "NASA", "screen_name": "NASA", "blue_verified": true }
      }
    }
  ]
}

SandBase X tweet-detail API reference showing the tweet_id parameter and response schema tweet-detail returns engagement counts and an embedded author object.

Step 3 — Profile the author

When you need the full account picture — follower count, account age, verification — read the profile by screen_name:

profile = call("web/user-profile", {"screen_name": "NASA"})
print(profile.get("name"), profile.get("rest_id"), profile.get("statuses_count"))
# example output: NASA 11348282 74334
print("verified:", profile.get("blue_verified"), "| since", profile.get("created_at"))
# example output: verified: True | since Wed Dec 19 20:20:32 +0000 2007

The field names and values above are illustrative — only the envelope is guaranteed, so the code reads with .get() and you should confirm the business fields against a live response. user-profile also accepts a rest_id as an optional input, so you can store the one you get back and re-read the profile later without depending on the screen name staying the same.

SandBase X user-profile API reference showing the screen_name parameter and response schema user-profile returns follower, verification, and account-age signals.

Putting it together

A minimal monitoring pass looks like this:

search = call("web/search-timeline", {"keyword": "NASA", "search_type": "Top"})
report = []

for tweet_id in extract_tweet_ids(search):  # extract_tweet_ids: your parser for the search response shape
    tweet = call("web/tweet-detail", {"tweet_id": tweet_id})
    author = tweet.get("author", {})
    report.append({
        "tweet_id": tweet.get("id"),
        "text": tweet.get("display_text"),
        "views": tweet.get("views"),
        "retweets": tweet.get("retweets"),
        "author": author.get("screen_name"),
        "author_verified": author.get("blue_verified", False),
    })
    # only spend a user-profile call when you need the full account picture

The code above reads every business field with .get(), since only the envelope is guaranteed and the illustrative fields may be absent or named differently in a live response. extract_tweet_ids is pseudocode — implement it against the actual search response shape once you have inspected a real response. Because each of the three web calls in this tutorial shares the same envelope and the same call helper, adding retries or rate-limit backoff is a one-place change. For higher-volume collection there is a separate twitter/bulk/tweet-search model, but note it is a distinct asynchronous surface: it runs through the Unified Run API (POST /v1/run with model: "twitter/bulk/tweet-search" and status polling), not the synchronous call() helper used here.

Handling the rough edges

  • Reuse the embedded author. In the illustrative shape, tweet-detail already carries a basic author; only call user-profile when you need follower counts or account age. Confirm the fields against a live response.
  • Page deliberately. search-timeline accepts an optional cursor request parameter; carry it over from the previous request rather than assuming one page is the whole result set.
  • Branch on status. A failed or timeout run carries error and no outputs. The call helper already enforces this.
  • Respect rate limits. As a client-side resilience measure, retry with backoff on transient errors such as HTTP 429.
  • Public data only. No X developer tier of your own, no posting, replies, or DMs, and no protected accounts — though you do authenticate every call with a SandBase API key.

Why run monitoring at the API layer

You could watch a search column in a client app, but that does not give you structured, storable data. Running these three calls on a schedule turns a live feed into a measurable signal: view and retweet counts you can trend, tweets you can dedupe by tweet_id, and authors you can weight by follower count and verification. Because the calls return named JSON fields, each pass drops cleanly into a table you can diff against the last one — new spikes, new voices, and shifts in how a topic is landing.

The same uniform envelope keeps the workflow composable. Swap the keyword for any topic, add a post-comments call to read a thread’s replies, and it slots in behind the same call helper with the same error handling. That is the payoff of a single-key, single-shape API for monitoring: your time goes to interpreting the signal, not to keeping a fragile pipeline alive.

FAQ

Do I need an X developer tier or OAuth? No. You authenticate to SandBase with your SANDBASE_API_KEY. These read endpoints need no X developer account and no OAuth on your side.

How do I page through search results? search-timeline accepts an optional cursor request parameter you carry over from the previous request to fetch the next page. Inspect one real response to see where the next cursor value sits, and page deliberately rather than assuming one call is the whole result set.

How do I handle higher-volume collection? The three steps here are synchronous single reads. For higher volume there is a separate twitter/bulk/tweet-search model, but it is a distinct asynchronous surface: it runs through the Unified Run API (POST /v1/run with status polling), not the synchronous call() helper used in this workflow.

Next steps

You now have a repeatable topic-to-author monitoring workflow built on three public, read-only calls.