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-contextand read the completed task's result pages. Group byconversation_idand connect direct parents usingin_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 need | SocQ endpoint | Starting input |
|---|---|---|
| The selected post itself | X Posts | Public post URLs |
| Replies to a selected post | X Post Replies | Public post URLs |
| The target, earlier reply-chain posts and available replies | X Thread Context | Public post URLs |
| Posts shared inside a community | X Community Posts | Public 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:
| Field | Meaning | Processing |
|---|---|---|
conversation_id | The conversation containing the post | Group records with the same value |
in_reply_to_id | The ID of the direct parent post | Look up that ID and connect the posts |
id | The current post's identifier | Index and deduplicate posts |
The following illustrative data explains the relationships:
| Post | Author | Conversation | Direct parent |
|---|---|---|---|
| A | Alice | A | None shown |
| B | Alice | A | A |
| C | Bob | A | B |
| D | Alice | A | C |
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_datasetindicating 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
- SocQ Thread Context documentation — public inputs, caps and standard relationship fields.
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