Content Extract
Asynchronously extract text content from URLs including web articles, Twitter/X profiles, tweets, YouTube videos, and WeChat Official Account posts.
Content Extract
Extract text content from any URL asynchronously. Submit a URL to create a task, then poll for the result.
Supported Sources
X / Twitter
Profile pages and individual tweets — fetch recent posts from any public account, or extract a single tweet with its full context.
YouTube
Extract video transcripts and metadata from any public YouTube video URL.
WeChat Official Accounts
Extract article content from WeChat Official Account posts (mp.weixin.qq.com).
Web Articles
Any publicly accessible web page — article text, metadata, and reference links.
Use Cases
- Podcast source material -- extract articles, tweets, or WeChat posts as input for podcast generation
- Content summarization -- pull long-form content and generate a summary in one call with
summarize - Social media monitoring -- batch-extract tweets from key accounts
- Research aggregation -- collect and structure content from multiple URLs
Create Extraction Task
POST /v1/content/extract
Submitting a task is non-blocking: the endpoint validates the request, reserves credits, kicks off extraction, and returns a taskId immediately. Use Query Task Status to retrieve the result.
Request example:
curl -X POST "https://api.marswave.ai/openapi/v1/content/extract" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "url",
"uri": "https://example.com/article"
}
}'const response = await fetch('https://api.marswave.ai/openapi/v1/content/extract', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
source: {
type: 'url',
uri: 'https://example.com/article',
},
}),
});
const data = await response.json();
const taskId = data.data.taskId;
console.log('Task ID:', taskId);import os
import requests
response = requests.post(
'https://api.marswave.ai/openapi/v1/content/extract',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
json={
'source': {
'type': 'url',
'uri': 'https://example.com/article',
}
}
)
data = response.json()
task_id = data['data']['taskId']
print('Task ID:', task_id)With options (summarize + max length):
Set summarize: true to have the extracted text condensed into an AI summary, and maxLength to cap the number of characters returned.
curl -X POST "https://api.marswave.ai/openapi/v1/content/extract" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "url",
"uri": "https://example.com/long-article"
},
"options": {
"summarize": true,
"maxLength": 5000
}
}'const response = await fetch('https://api.marswave.ai/openapi/v1/content/extract', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
source: {
type: 'url',
uri: 'https://example.com/long-article',
},
options: {
summarize: true,
maxLength: 5000,
},
}),
});
const data = await response.json();
console.log(data);import os
import requests
response = requests.post(
'https://api.marswave.ai/openapi/v1/content/extract',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
json={
'source': {
'type': 'url',
'uri': 'https://example.com/long-article',
},
'options': {
'summarize': True,
'maxLength': 5000,
},
}
)
data = response.json()
print(data)Twitter/X profile URL with tweet count:
# For Twitter/X profile URLs, use the twitter option to control how many tweets to fetch
curl -X POST "https://api.marswave.ai/openapi/v1/content/extract" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "url",
"uri": "https://x.com/elonmusk"
},
"options": {
"twitter": {
"count": 50
}
}
}'const response = await fetch('https://api.marswave.ai/openapi/v1/content/extract', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
source: {
type: 'url',
uri: 'https://x.com/elonmusk',
},
options: {
twitter: {
count: 50,
},
},
}),
});
const data = await response.json();
console.log(data);import os
import requests
response = requests.post(
'https://api.marswave.ai/openapi/v1/content/extract',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
json={
'source': {
'type': 'url',
'uri': 'https://x.com/elonmusk',
},
'options': {
'twitter': {
'count': 50,
},
},
}
)
data = response.json()
print(data)WeChat Official Account article:
curl -X POST "https://api.marswave.ai/openapi/v1/content/extract" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": {
"type": "url",
"uri": "https://mp.weixin.qq.com/s/XXXXXXXXXXXXXXXX"
}
}'const response = await fetch('https://api.marswave.ai/openapi/v1/content/extract', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
source: {
type: 'url',
uri: 'https://mp.weixin.qq.com/s/XXXXXXXXXXXXXXXX',
},
}),
});
const data = await response.json();
const taskId = data.data.taskId;import os
import requests
response = requests.post(
'https://api.marswave.ai/openapi/v1/content/extract',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
json={
'source': {
'type': 'url',
'uri': 'https://mp.weixin.qq.com/s/XXXXXXXXXXXXXXXX',
}
}
)
data = response.json()
task_id = data['data']['taskId']Request body:
| Field | Type | Required | Description |
|---|---|---|---|
source | object | Yes | Source to extract from |
source.type | string | Yes | Must be "url" |
source.uri | string | Yes | The URL to extract content from |
options | object | No | Extraction options (defaults to {}) |
options.summarize | boolean | No | Generate an AI summary of the extracted text (default false) |
options.maxLength | integer | No | Maximum content length in characters (default 100000, min 1, max 500000) |
options.twitter | object | No | Twitter/X specific options |
options.twitter.count | integer | No | Number of tweets to fetch from a profile URL (1–100, default 20) |
Response example:
{
"code": 0,
"message": "success",
"data": {
"taskId": "67f6a1b2c3d4e5f6a7b8c9d0"
}
}The taskId is a 24-character hex string. Pass it to the status endpoint below.
Query Task Status
GET /v1/content/extract/{taskId}
Poll this endpoint until status is completed or failed. While the task is still running, the response carries only the status — the extracted content is withheld until extraction and credit settlement finish.
Request example:
curl -X GET "https://api.marswave.ai/openapi/v1/content/extract/67f6a1b2c3d4e5f6a7b8c9d0" \
-H "Authorization: Bearer $LISTENHUB_API_KEY"const result = await fetch(`https://api.marswave.ai/openapi/v1/content/extract/${taskId}`, {
headers: {
'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
},
});
const data = await result.json();
console.log('Status:', data.data.status);import os
import requests
result = requests.get(
f'https://api.marswave.ai/openapi/v1/content/extract/{task_id}',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'}
)
data = result.json()
print('Status:', data['data']['status'])Path parameters:
| Field | Type | Description |
|---|---|---|
taskId | string | 24-character hex string returned by the create endpoint |
Response while processing (status: "processing"):
{
"code": 0,
"message": "success",
"data": {
"taskId": "67f6a1b2c3d4e5f6a7b8c9d0",
"status": "processing",
"createdAt": 1744200000000
}
}The data object (extracted content) is omitted until the task completes.
Response when complete (status: "completed"):
{
"code": 0,
"message": "success",
"data": {
"taskId": "67f6a1b2c3d4e5f6a7b8c9d0",
"status": "completed",
"createdAt": 1744200000000,
"data": {
"content": "The extracted article text content...",
"metadata": {
"title": "Article Title",
"author": "Author Name"
},
"references": [
"https://example.com/related-article"
]
},
"credits": 100
}
}Response on failure (status: "failed"):
{
"code": 0,
"message": "success",
"data": {
"taskId": "67f6a1b2c3d4e5f6a7b8c9d0",
"status": "failed",
"createdAt": 1744200000000,
"failCode": 1001,
"message": "Failed to extract content from URL"
}
}Response fields:
| Field | Type | Description |
|---|---|---|
taskId | string | Task identifier |
status | string | processing, completed, or failed |
createdAt | integer | Creation time as a 13-digit epoch-millisecond timestamp |
data | object | Extracted content. Present only when status is completed |
data.content | string | Extracted text content (summarized when summarize: true) |
data.metadata | object | Page metadata such as title and author |
data.references | array | Referenced URLs found in the content |
credits | integer | Credits consumed (present when status is completed) |
failCode | integer | Error code (present when status is failed) |
message | string | Error description (present when status is failed) |
Notes
Twitter/X profile URLs:
- When the source URL is a Twitter/X profile (e.g.
https://x.com/username), the API fetches recent tweets. - Use
options.twitter.countto control how many tweets to retrieve (1–100, default20). - This option is ignored for non-Twitter URLs.
WeChat Official Accounts:
- Use the full
mp.weixin.qq.com/s/...article URL. - Extracted content includes the article body text and metadata.
Task lifecycle:
| Status | Description |
|---|---|
processing | Extraction is in progress |
completed | Content extracted successfully |
failed | Extraction failed -- check failCode and message |
Polling recommendations:
- Initial wait: 5 seconds after task creation
- Poll interval: 5 seconds
- Typical completion time: 10-30 seconds depending on URL complexity
Credits:
Content extraction reserves credits when the task starts and settles to the actual amount once the content length is known. The credits field in the completed response reflects the final charge.
| Rule | Details |
|---|---|
| Pre-deduction | A small hold is reserved when the task starts |
| Actual charge | Based on the number of characters in the extracted content, capped by maxLength |
| Failure refund | All reserved credits are refunded if extraction fails |
Content extraction itself has no estimate endpoint — the final credits value is known only after the content length settles. Check your live balance with GET /v1/user/subscription, and see Pricing for the credit-to-feature mapping.