XTwitter ThreadsNested RepliesConversation IDAPI Tutorial

Twitter Thread API: Get Context and Nested Replies

Get Twitter/X thread context with SocQ. Group conversations, connect replies and filter author threads with Python code, then export JSON for review.

SocQUpdated October 16, 20268 min read

Bring target posts, preceding context and reply relationships together with SocQ. Organize Twitter/X discussions by who replied to whom, then use conversation and direct-parent IDs for reading, review, summaries and author-thread filtering.

The SocQ X Thread Context API queries the target public post, earlier reply-chain posts and source-returned replies. This guide covers collection, conversation grouping, parent-child exports and author-thread filtering.

Quick answer: Submit a public post URL to POST /v1/x/thread-context and read the completed task's result pages. Group by conversation_id and connect direct parents using in_reply_to_id. When a parent's body is absent, retain its reference and mark the gap.

Choose Post Lookup, Replies or Thread Context

What you needSocQ endpointStarting input
The selected post itselfX PostsPublic post URLs
Replies to a selected postX Post RepliesPublic post URLs
The target, earlier reply-chain posts and available repliesX Thread ContextPublic post URLs
Posts shared inside a communityX Community PostsPublic community URLs

Use Thread Context to read the selected post's preceding discussion. "Earlier posts" means posts reached by following direct-parent relationships backwards.

For example, if B replies to A and C replies to B, C's parent chain is C → B → A. Conversation IDs determine grouping, parent-post IDs determine connections, and publication times provide chronological display order.

After collection, pass the text and relationship data to a reading interface or a separate summarization workflow.

Retrieve Thread Context from a Public Post URL

Use a public /status/{numeric_id} URL, replacing the example with the post you want to research:

curl -X POST "https://api.socq.ai/v1/x/thread-context" \
  -H "Authorization: Bearer $SOCQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "urls": ["https://x.com/BaganiTohid/status/2110635249889919362"],
    "results_limit": 100
  }'

The input accepts public X and Twitter post links, including a reply from the middle of a discussion. Put the full /status/{numeric_id} URL in urls.

The response's data.task_id identifies the background task. Retain it and request:

curl "https://api.socq.ai/v1/tasks/$TASK_ID?limit=100" \
  -H "Authorization: Bearer $SOCQ_API_KEY"

Wait while the task is queued, running or retrying. On failed, inspect data.error_message. When succeeded, read data.results.items and continue with next_cursor while has_more is true.

results_limit defaults to 100 and accepts integers from 1 to 2000 for each input URL. Two URLs with a cap of 100 each request up to 100 records independently. Deduplicate by post ID when merging captures of the same conversation.

Thread Context accepts any integer within that limit range. Build parent-child connections from the relationship fields after retrieval, and use array order to iterate over the records.

Understand Conversation IDs and Parent-Post IDs

The two relationship fields serve different purposes:

FieldMeaningProcessing
conversation_idThe conversation containing the postGroup records with the same value
in_reply_to_idThe ID of the direct parent postLook up that ID and connect the posts
idThe current post's identifierIndex and deduplicate posts

The following illustrative data explains the relationships:

PostAuthorConversationDirect parent
AAliceANone shown
BAliceAA
CBobAB
DAliceAC

All four posts belong to conversation A, and D's direct parent is C. Connecting by parent ID preserves the D → C → B → A reply path.

See the Thread Context field reference for definitions. Use the conversation ID and direct-parent ID for their respective grouping and linking steps.

Standard records also contain text, authors, publication and collection times, media and engagement counts. Source-omitted fields remain null.

Collect the Stored Result Pages with Python

Save this standard-library helper as socq_collect.py:

import json
import os
import time
from urllib.parse import urlencode
from urllib.request import Request, urlopen

BASE = "https://api.socq.ai/v1"
PENDING = {"queued", "running", "retrying"}

def api(method, path, payload=None):
    body = json.dumps(payload).encode("utf-8") if payload is not None else None
    request = Request(
        BASE + path, data=body, method=method,
        headers={
            "Authorization": "Bearer " + os.environ["SOCQ_API_KEY"],
            "Content-Type": "application/json",
        },
    )
    with urlopen(request, timeout=60) as response:
        result = json.load(response)
    if result.get("code") != 200:
        raise RuntimeError(result)
    return result["data"]

def collect(endpoint, payload):
    task_id = api("POST", "/" + endpoint, payload)["task_id"]
    print("Task:", task_id, flush=True)
    path = "/tasks/" + task_id
    deadline = time.monotonic() + 600
    data = api("GET", path + "?limit=100")
    while data["status"] in PENDING:
        if time.monotonic() >= deadline:
            raise TimeoutError("Resume GET " + path + "; do not resubmit automatically")
        time.sleep(2)
        data = api("GET", path + "?limit=100")
    if data["status"] != "succeeded":
        raise RuntimeError(data.get("error_message") or data["status"])

    items, seen_cursors = [], set()
    while True:
        if time.monotonic() >= deadline:
            raise TimeoutError("Resume pagination for " + task_id)
        page = data["results"]
        items.extend(page["items"])
        if not page["has_more"]:
            return task_id, items
        cursor = page.get("next_cursor")
        if not cursor or cursor in seen_cursors:
            raise RuntimeError("Missing or repeated result cursor")
        seen_cursors.add(cursor)
        data = api("GET", path + "?" + urlencode({"limit": 100, "cursor": cursor}))
        if data["status"] != "succeeded":
            raise RuntimeError("Task is not ready for result pagination")

Save the next script as collect_thread.py in the same directory:

import json
import sys
from socq_collect import collect

if len(sys.argv) != 2:
    raise SystemExit("Usage: python3 collect_thread.py PUBLIC_POST_URL")
payload = {"urls": [sys.argv[1]], "results_limit": 100}
task_id, posts = collect("x/thread-context", payload)
with open("thread-context.json", "w", encoding="utf-8") as output:
    json.dump({"task_id": task_id, "input": payload, "items": posts},
              output, ensure_ascii=False, indent=2)

Set SOCQ_API_KEY, then run it with the selected public post:

python3 collect_thread.py \
  "https://x.com/BaganiTohid/status/2110635249889919362"

thread-context.json saves the input links, task ID and records. The helper waits up to ten minutes on the client. After a timeout, retain the printed task ID and continue checking the server task.

Pagination reads the records saved by that task. Reading every page gives you its complete saved result set; the records and gap markers describe the conversation coverage.

Build Reply Relationships by Direct-Parent ID

Index posts by ID, then add parent-child links. When a referenced parent body is absent, retain the ID and set parent_in_dataset to false.

Save this as build_thread.py:

import json
import sys

if len(sys.argv) not in (2, 3):
    raise SystemExit("Usage: python3 build_thread.py INPUT_JSON [THREAD_AUTHOR_ID]")
with open(sys.argv[1], encoding="utf-8") as source:
    records = json.load(source)["items"]
author_id = sys.argv[2] if len(sys.argv) == 3 else None

nodes, missing_id_records = {}, []
for post in records:
    if post.get("id"):
        nodes[str(post["id"])] = post
    else:
        missing_id_records.append(post)

edges, groups, unknown_conversation_ids, unknown_parent_ids = [], {}, [], []
for post_id, post in nodes.items():
    conversation_id = post.get("conversation_id")
    if conversation_id:
        groups.setdefault(str(conversation_id), []).append(post_id)
    else:
        unknown_conversation_ids.append(post_id)
    parent_id = post.get("in_reply_to_id")
    if parent_id is None:
        unknown_parent_ids.append(post_id)
    else:
        parent_id = str(parent_id)
        edges.append({
            "parent_id": parent_id, "child_id": post_id,
            "parent_in_dataset": parent_id in nodes,
        })

self_reply_edges = []
if author_id:
    for edge in edges:
        parent = nodes.get(edge["parent_id"])
        child = nodes[edge["child_id"]]
        if parent and edge["parent_id"] != edge["child_id"]:
            parent_author = (parent.get("author") or {}).get("id")
            child_author = (child.get("author") or {}).get("id")
            if parent_author == author_id and child_author == author_id:
                self_reply_edges.append(edge)

graph = {
    "nodes": list(nodes.values()), "edges": edges, "conversation_groups": groups,
    "missing_parent_ids": sorted({
        edge["parent_id"] for edge in edges if not edge["parent_in_dataset"]
    }),
    "unknown_conversation_post_ids": unknown_conversation_ids,
    "unknown_parent_post_ids": unknown_parent_ids,
    "records_without_id": missing_id_records,
    "selected_author_id": author_id, "selected_author_self_reply_edges": self_reply_edges,
}
with open("thread-graph.json", "w", encoding="utf-8") as output:
    json.dump(graph, output, ensure_ascii=False, indent=2)

Build the relationship export:

python3 build_thread.py thread-context.json

The resulting thread-graph.json contains:

  • Unique identifiable post nodes, plus separately retained records without IDs.
  • Parent-child edges with parent_in_dataset indicating whether the parent is present.
  • Groups for known conversation IDs and a separate list for missing conversation IDs.
  • Referenced parent IDs that do not have a corresponding collected post.
  • Post IDs whose direct-parent field is null.

in_reply_to_id=null means the record provides no direct-parent reference; the program adds it to unknown_parent_post_ids. When a parent ID exists but its record is absent from the index, retain the edge and mark the missing parent.

The export preserves conversation groups, known edges and missing-record markers. A reading interface can display connected branches and context gaps separately.

Keep an Author's Thread Separate from Reader Replies

In the illustrative data, Alice wrote A, B and D. A-to-B is an author self-reply segment, while D is Alice's reply to Bob. Filter by author ID to read all of the author's statements, then check both parent and child author IDs to identify self-reply segments.

When you know the thread author's account ID, pass that string as the second argument:

python3 build_thread.py thread-context.json THREAD_AUTHOR_ID

selected_author_self_reply_edges stores edges whose parent and child belong to the specified author. Confirm that author ID before filtering; the input link can identify the author's post or a reader's reply.

Select a connected self-reply segment around the author post you want to read. If the author has several self-replies to one parent, the export retains those branches so the reading interface can select its display scope.

Keep the complete records and author-filtered view together, so later reading filters can use the original data.

Export Context for Reading, Review or Summarization

Show source URLs, authors, publication times and parent relationships in the reading interface, with markers for missing parents. Save the relationship JSON beside text or Markdown exports for later structural reconstruction.

A summarization workflow reads the post text and relationships, handles author statements and reader responses separately, and retains context-gap markers. Thread Context supplies the records; your model or application generates the summary.

When merging tasks from the same conversation, deduplicate by post ID and retain their input links and task IDs. Keep previously observed posts under their original capture times when they are absent from a later capture.

To discover discussions before examining them, use the community collection tutorial or the X search and collection guide.

Use One Context Dataset for Reading, Review and Summaries

SocQ Thread Context puts post text and relationship IDs in the same standard results. Use the code to group conversations, export reply relationships and build an author-filtered view. Reading interfaces and summary workflows can reuse those records with their sources and context-gap markers.

Open Thread Context and explore your target discussion.

Sources and Further Reading

FAQ

Can I start from a reply instead of the first post?

Yes. Put that public reply URL in urls. The task reads the target, public preceding posts and source-returned replies up to the configured cap.

Is this the same as getting replies to a post?

Post Replies focuses on replies to the selected post. Thread Context reads the target, preceding posts and replies to organize context through reply relationships.

Does the API return every reply in a conversation?

The task collects public records up to each input's results_limit. Reading all pages retrieves the posts saved in that capture; assess discussion coverage using the missing-parent and reply records as well.

How do I handle replies to replies?

Connect direct parents with in_reply_to_id and group conversations with conversation_id. Preserve a missing parent's ID and display a gap marker.

Why is a referenced parent missing?

The parent reference was returned, but its body is absent from this capture. Keep the reference and query that post when you need more context; use source-reported information to establish a specific reason for its absence.

How do I keep only the original author's thread?

Confirm the author ID and retain edges whose parent and child both belong to that author. Select a connected segment around the target post, and choose the display scope when self-replies branch.

Can I save the thread for an AI summary?

Yes. Save collected text, authors, times and relationships as JSON, then run a separate summarization step. Preserve missing-context markers and source URLs alongside the summary.

X API

Test the workflow with a public X URL

Submit public inputs and receive normalized records with traceable source context.

Explore X APIs