Getting Started
The Scrapely API allows you to send direct messages and fetch conversations programmatically. Use it to integrate Twitter DM automation into your own applications and workflows.
Base URL
https://app.scrapely.co/api/v1Available Endpoints
/accountsList your Twitter accounts/accountsAdd a new account/accountsRemove an account/accounts/cookiesUpdate account cookies/accounts/proxyUpdate account proxy/accounts/pauseToggle account pause status/accounts/pause-followupsToggle follow-ups pause status/get-cookiesQueue cookie extraction job (Deprecated)/get-cookies/statusCheck extraction status (Deprecated)/scraping-sourcesCreate a scraping source/scraping-sourcesList/check scraping status/campaignsLaunch a DM campaign/campaignsList campaigns/campaigns/analyticsGet campaign analytics/conversationsFetch conversations/dm/sendSend a direct message/crm/conversationsGet CRM conversations by tag/crm/updateUpdate CRM data/scheduled-tweetsSchedule a tweet/scheduled-tweetsList scheduled tweets/scheduled-tweetsCancel scheduled tweetQuick Start
1. Generate an API key from your Settings page
2. Make your first API call:
curl -H "X-API-Key: YOUR_API_KEY" \
https://app.scrapely.co/api/v1/accountsOpenAPI Specification
Download our OpenAPI spec to generate typed clients for any language:
# Generate TypeScript client
npx openapi-typescript-codegen --input https://app.scrapely.co/openapi.yaml --output ./api
# Generate Python client
pip install openapi-python-client
openapi-python-client generate --url https://app.scrapely.co/openapi.yaml
# Import into Postman
# File -> Import -> Link -> https://app.scrapely.co/openapi.yamlAuthentication
All API requests require authentication using an API key. Generate API keys from your Settings page.
X-API-Key Header (Recommended)
curl -H "X-API-Key: sk_live_your_api_key" \
https://app.scrapely.co/api/v1/accountsAuthorization Bearer Header
curl -H "Authorization: Bearer sk_live_your_api_key" \
https://app.scrapely.co/api/v1/accountsNever expose your API key in client-side code or public repositories.
List Accounts
/accountsList your connected Twitter accountsReturns a list of all active Twitter accounts. Use the account id when sending DMs.
Request
curl -H "X-API-Key: YOUR_API_KEY" \
https://app.scrapely.co/api/v1/accountsResponse
{
"accounts": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"handle": "yourhandle",
"name": "Your Name",
"profile_image_url": "https://pbs.twimg.com/...",
"is_active": true,
"is_verified": true,
"dms_sent": 1250,
"created_at": "2024-01-15T10:30:00Z",
"cookies": [
{ "name": "auth_token", "value": "abc123...", "domain": ".x.com" },
{ "name": "ct0", "value": "xyz789...", "domain": ".x.com" }
],
"proxy": "161.77.187.46:12323",
"proxy_auth": "username:password",
"is_paused": false,
"followups_paused": false,
"warmup_paused": false,
"ai_setter_enabled": true
}
]
}Response Fields
| Field | Type | Description |
|---|---|---|
| is_paused | boolean | Whether DM sending is paused for this account |
| followups_paused | boolean | Whether follow-up messages are paused |
| warmup_paused | boolean | Whether account warmup is paused |
| ai_setter_enabled | boolean | Whether AI setter is enabled for this account |
Add Account
/accountsAdd a new Twitter accountAdd a new Twitter account to your workspace. Requires cookies exported from your browser and a proxy configuration.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| handle | string | Required | Twitter handle (with or without @) |
| cookies | array | Required | Array of cookie objects (Chrome export format) |
| proxy | string | Required | Proxy URL (e.g., http://proxy.example.com:8080) |
| proxy_auth | string | Required | Proxy authentication (username:password) |
| xchat_pin | string | Required | X chat PIN code |
Request
curl -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"handle": "myhandle",
"cookies": [
{"name": "ct0", "value": "abc123...", "domain": ".twitter.com"},
{"name": "auth_token", "value": "xyz789...", "domain": ".twitter.com"}
],
"proxy": "http://proxy.example.com:8080",
"proxy_auth": "username:password",
"xchat_pin": "123456"
}' \
https://app.scrapely.co/api/v1/accountsResponse
{
"success": true,
"message": "Account created successfully",
"account": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"handle": "myhandle",
"is_active": true,
"created_at": "2024-01-15T10:30:00Z",
"is_new": true
}
}Cookies must include at minimum ct0 and auth_token. Use a browser extension like "EditThisCookie" to export cookies from Twitter.
Remove Account
/accountsRemove a Twitter accountRemove a Twitter account from your workspace. You can identify the account by either its UUID or Twitter handle.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| account_id | string | Required* | The account UUID to remove |
| handle | string | Required* | Twitter handle to remove (with or without @) |
*Either account_id or handle is required.
Request (by ID)
curl -X DELETE \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_id": "550e8400-e29b-41d4-a716-446655440000"
}' \
https://app.scrapely.co/api/v1/accountsRequest (by Handle)
curl -X DELETE \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"handle": "myhandle"
}' \
https://app.scrapely.co/api/v1/accountsResponse
{
"success": true,
"message": "Account removed successfully",
"account": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"handle": "myhandle"
}
}Removing an account will permanently delete it from your workspace. All associated data including DM history will be retained, but the account will no longer be available for sending messages.
Update Proxy
/accounts/proxyUpdate proxy settings for an existing accountUpdate the proxy configuration for an existing Twitter account. Each account uses its own proxy to avoid detection.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| account_id | string | Required | The account UUID |
| proxy | string | Required | Proxy URL (e.g., "161.77.187.46:12323") |
| proxy_auth | string | Required | Proxy authentication (username:password) |
Request
curl -X PUT \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_id": "550e8400-e29b-41d4-a716-446655440000",
"proxy": "161.77.187.46:12323",
"proxy_auth": "username:password"
}' \
https://app.scrapely.co/api/v1/accounts/proxyResponse
{
"success": true,
"message": "Proxy updated successfully",
"account": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"handle": "myhandle",
"proxy": "161.77.187.46:12323",
"proxy_auth": "username:password"
}
}Toggle Pause Status
/accounts/pausePause or unpause DM sending for an accountPause or unpause DM sending for a specific Twitter account. When paused, the account will not send any DMs until unpaused.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| account_id | string | Required | The account UUID |
| is_paused | boolean | Required | Set to true to pause, false to unpause |
Request
curl -X PATCH \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_id": "550e8400-e29b-41d4-a716-446655440000",
"is_paused": true
}' \
https://app.scrapely.co/api/v1/accounts/pauseResponse
{
"success": true,
"message": "Account paused",
"account": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"handle": "myhandle",
"is_paused": true
}
}Toggle Follow-ups Pause Status
/accounts/pause-followupsPause or unpause follow-up messages for an accountPause or unpause follow-up messages for a specific Twitter account. When paused, the account will not send any follow-up messages until unpaused.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| account_id | string | Required | The account UUID |
| followups_paused | boolean | Required | Set to true to pause, false to unpause |
Request
curl -X PATCH \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_id": "550e8400-e29b-41d4-a716-446655440000",
"followups_paused": true
}' \
https://app.scrapely.co/api/v1/accounts/pause-followupsResponse
{
"success": true,
"message": "Follow-ups paused",
"account": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"handle": "myhandle",
"followups_paused": true
}
}Create Scraping Source
/scraping-sourcesCreate a new lead source by scraping TwitterCreate a lead source by scraping followers and/or following from Twitter accounts. Credits are consumed based on the number of leads scraped.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Required | Name for the lead source |
| sources | array | Required | Array of scraping targets |
| filters | object | Optional | Filter configuration to filter scraped leads |
Source Object
| Parameter | Type | Description |
|---|---|---|
| handle | string | Twitter handle to scrape |
| followers | boolean | Scrape followers |
| following | boolean | Scrape following |
All followers/following will be scraped automatically. Credits are deducted as leads are imported.
Filters Object
Apply filters to scraped leads in real-time during the scraping process. Only leads matching your filter criteria will be saved and count against your credits.
| Parameter | Type | Description |
|---|---|---|
| includeKeywords | array | Array of keyword groups. Each group is an array of keywords. Leads must match keywords in their bio/description. |
| excludeKeywords | array | Array of keyword groups to exclude. Leads matching these keywords will be filtered out. |
| includeWithinLogic | string | "AND" or "OR". Logic within each include keyword group. Default: "AND" |
| includeBetweenLogic | string | "AND" or "OR". Logic between include keyword groups. Default: "OR" |
| excludeWithinLogic | string | "AND" or "OR". Logic within each exclude keyword group. Default: "AND" |
| excludeBetweenLogic | string | "AND" or "OR". Logic between exclude keyword groups. Default: "OR" |
| locationExcludeKeywords | array | Array of location keywords to exclude (e.g., country names). |
| followers | object | { min: number, max: number }. Only keep leads who have this many followers themselves. |
| following | object | { min: number, max: number }. Only keep leads who are following this many accounts. |
| hasWebsite | boolean | true = only leads with website, false = only leads without website, null = no filter. |
Request
curl -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Tech Founders Q1",
"sources": [
{
"handle": "paulg",
"followers": true
},
{
"handle": "naval",
"followers": true,
"following": true
}
],
"filters": {
"includeKeywords": [["founder", "ceo"], ["startup"]],
"includeBetweenLogic": "OR",
"includeWithinLogic": "AND",
"excludeKeywords": [["hiring", "recruiter"]],
"followers": { "min": 100, "max": 50000 },
"hasWebsite": true
}
}' \
https://app.scrapely.co/api/v1/scraping-sourcesResponse
{
"success": true,
"lead_source_id": "uuid-here",
"name": "Tech Founders Q1",
"jobs_created": 3,
"message": "Scraping all followers/following. Credits deducted as leads are imported.",
"jobs": [
{
"id": "job-uuid-1",
"type": "followers",
"handle": "paulg",
"status": "pending"
},
{
"id": "job-uuid-2",
"type": "followers",
"handle": "naval",
"status": "pending"
},
{
"id": "job-uuid-3",
"type": "following",
"handle": "naval",
"status": "pending"
}
]
}Check Scraping Status
/scraping-sourcesList scraping sources or get statusCheck the status of your scraping jobs. Pass an id parameter to get detailed status for a specific source.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| id | string | Get specific source by ID |
| limit | number | Results per page (default: 20, max: 100) |
| offset | number | Pagination offset |
Request
curl -H "X-API-Key: YOUR_API_KEY" \
"https://app.scrapely.co/api/v1/scraping-sources?id=uuid-here"Response
{
"scraping_source": {
"id": "uuid-here",
"name": "Tech Founders Q1",
"status": "processing",
"leads_imported": 4200,
"total_leads": 9000,
"progress": 47,
"handles": [
{
"handle": "paulg",
"followers": {
"status": "completed",
"leads_imported": 5000,
"max_leads": 5000
},
"following": null
}
],
"created_at": "2024-01-15T10:30:00Z"
}
}pending- Job queued, not startedprocessing- Currently scrapingcompleted- Successfully finishedfailed- Error occurred
Launch Campaign
/campaignsLaunch a DM campaignLaunch a DM campaign targeting leads from your scraping sources. Supports A/B testing with multiple message variants. Maximum 25,000 leads per campaign.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Required | Campaign name |
| message | string | Required* | Message text (supports {{firstName}}) |
| message_variants | array | Optional | Message variants for A/B testing (max 5) |
| lead_source_ids | array | Required | Lead source IDs to target |
| account_ids | array | Optional | Account IDs to send from (defaults to all) |
| followups | array | Optional | Follow-up messages |
| max_leads | number | Optional | Maximum leads to target (max 25,000) |
| enable_follow | boolean | Optional | Follow the lead's profile before DM (default: false) |
| enable_like | boolean | Optional | Like the lead's recent post before DM (default: false) |
| enable_comment | boolean | Optional | Comment on the lead's post before DM (default: false) |
| comment_template | string | Optional | AI personalization template for auto-generated comments |
*Either message or message_variants is required.
Request
curl -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Outreach Campaign Q1",
"message_variants": [
{
"message": "Hey {{firstName}}, I saw your work and wanted to connect!",
"followups": [
{"wait_time": 3, "wait_unit": "days", "message": "Just bumping this up!"}
]
},
{
"message": "Hi {{firstName}}! Quick question about your business..."
}
],
"lead_source_ids": ["uuid-1", "uuid-2"],
"enable_follow": true,
"enable_like": true,
"enable_comment": true,
"comment_template": "Great insights on {{topic}}!"
}' \
https://app.scrapely.co/api/v1/campaignsResponse
{
"success": true,
"campaign_name": "Outreach Campaign Q1",
"jobs_created": 1500,
"leads_targeted": 1500,
"message_variants": 2,
"accounts_used": 2,
"distribution": [
{"account_id": "uuid", "handle": "account1", "jobs": 750},
{"account_id": "uuid", "handle": "account2", "jobs": 750}
]
}List Campaigns
/campaignsList campaigns with basic statsRequest
curl -H "X-API-Key: YOUR_API_KEY" \
"https://app.scrapely.co/api/v1/campaigns?limit=10"Response
{
"campaigns": [
{
"name": "Outreach Campaign Q1",
"total": 1500,
"completed": 1200,
"pending": 250,
"failed": 50,
"progress": 80
}
],
"pagination": {
"limit": 10,
"offset": 0,
"count": 1,
"total": 5
}
}Campaign Analytics
/campaigns/analyticsGet detailed campaign analyticsGet detailed analytics including reply rates, sentiment analysis, daily (24-hour) reply and positive reply counts, and per-variant A/B testing statistics. Returns the same data structure as the dashboard UI.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| campaign_name | string | Filter to specific campaign |
| account_id | string | Filter to specific account |
Request
curl -H "X-API-Key: YOUR_API_KEY" \
https://app.scrapely.co/api/v1/campaigns/analyticsResponse
{
"accounts": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"handle": "myhandle",
"name": "My Account",
"profile_image_url": "https://pbs.twimg.com/...",
"is_paused": false,
"dms_sent": 1500,
"dms_sent_24hrs": 45,
"replies_24hrs": 12,
"positive_replies_24hrs": 8,
"responses": 180,
"response_rate": 12.0,
"pending_count": 250,
"sentiment": {
"positive": 120,
"negative": 15,
"neutral": 45
}
}
],
"campaigns": [
{
"account_id": "550e8400-e29b-41d4-a716-446655440000",
"account_handle": "myhandle",
"campaign_name": "Outreach Q1",
"total": 1500,
"completed": 1200,
"pending": 250,
"failed": 50,
"remaining": 250,
"progress": 80,
"reply_count": 150,
"response_rate": 12.5
}
],
"campaignSentimentStats": [
{
"campaign_name": "Outreach Q1",
"account_id": "550e8400-e29b-41d4-a716-446655440000",
"sentiment": {
"positive": 100,
"negative": 12,
"neutral": 38,
"total": 150
},
"reply_count": 150
}
],
"campaignVariantStats": [
{
"campaign_name": "Outreach Q1",
"account_id": "550e8400-e29b-41d4-a716-446655440000",
"variants": [
{
"message": "Hey {{firstName}}, loved your recent post...",
"sent": 600,
"replied": 90,
"reply_rate": 15.0
},
{
"message": "Hi {{firstName}}, saw you're building...",
"sent": 600,
"replied": 60,
"reply_rate": 10.0
}
]
}
],
"totals": {
"dms_sent": 1500,
"dms_sent_24hrs": 45,
"replies_24hrs": 12,
"positive_replies_24hrs": 8,
"responses": 180,
"response_rate": 12.0,
"positive": 120,
"negative": 15,
"neutral": 45
}
}Fetch Conversations
/conversationsFetch DM conversationsReturns conversations with lead info. Pass a conversation_id to get a single conversation with all messages and complete lead data.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| conversation_id | string | Optional | Get single conversation with all messages & lead info |
| account_id | string | Optional | Filter by Twitter account |
| handle | string | Optional | Filter by receiver handle |
| limit | number | Optional | Results per page (max 100) |
| cursor | string | Optional | Pagination cursor (recommended) |
| include_messages | boolean | Optional | Include full message history (for list view) |
Use cursor instead of offset for reliable pagination. The next_cursor in the response can be passed as the cursor parameter.
List Conversations
curl -H "X-API-Key: YOUR_API_KEY" \
"https://app.scrapely.co/api/v1/conversations?limit=10"List Response
{
"conversations": [
{
"conversation_id": "123456789-987654321",
"account_handle": "yourhandle",
"receiver": {
"screen_name": "prospect",
"name": "John Doe",
"bio": "Founder @ Startup",
"website": "https://example.com",
"location": "San Francisco",
"followers_count": 5000
},
"last_message": {
"text": "Thanks for reaching out!",
"time": 1709234567000,
"is_sent": false
},
"is_unread": true
}
],
"pagination": {
"limit": 10,
"count": 10,
"has_more": true
},
"next_cursor": "eyJ0IjoxNzA5MjM0NTY3MDAwLCJpIjoiYWJjMTIzIn0"
}Get Single Conversation
Pass conversation_id to get full details:
curl -H "X-API-Key: YOUR_API_KEY" \
"https://app.scrapely.co/api/v1/conversations?conversation_id=123456789-987654321"Single Conversation Response
{
"conversation": {
"id": "uuid-here",
"conversation_id": "123456789-987654321",
"account_handle": "yourhandle",
"lead": {
"user_id": "987654321",
"screen_name": "prospect",
"name": "John Doe",
"profile_image_url": "https://pbs.twimg.com/...",
"profile_banner_url": "https://pbs.twimg.com/...",
"bio": "Founder @ Startup | Building cool things",
"location": "San Francisco, CA",
"website": "https://example.com",
"followers_count": 5000,
"following_count": 1200,
"verified": false
},
"crm": {
"notes": "Interested in enterprise plan",
"deal_value": 5000.00,
"deal_currency": "USD"
},
"stats": {
"message_count": 8,
"is_unread": false,
"last_message_time": 1709234567000,
"last_message_text": "Sounds great, let's schedule a call!",
"last_message_is_sent": false
},
"messages": [
{
"text": "Hey! Saw your work on...",
"time": 1709200000000,
"sender": "123456789",
"isSent": true
},
{
"text": "Thanks for reaching out!",
"time": 1709210000000,
"sender": "987654321",
"isSent": false
}
],
"created_at": "2024-02-28T10:00:00.000Z",
"updated_at": "2024-02-28T12:30:00.000Z"
}
}Send DM
/dm/sendSend a direct messageSend a direct message to an existing conversation.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| conversation_id | string | Required | The conversation ID |
| message | string | Required | Message text (max 10,000 chars) |
| account_id | string | Required | Twitter account ID to send from |
Request
curl -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "123456789-987654321",
"message": "Hey! Just following up.",
"account_id": "550e8400-e29b-41d4-a716-446655440000"
}' \
https://app.scrapely.co/api/v1/dm/sendResponse
{
"success": true,
"message_id": "1234567890123456789",
"conversation_id": "123456789-987654321",
"account_handle": "yourhandle"
}Get CRM Conversations
/crm/conversationsGet conversations organized by CRM tagsFetch conversations organized by CRM tags like "interested", "negative", "booked", etc.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| tag | string | Filter to specific tag |
| limit | number | Conversations per tag (default: 20, max: 100) |
| include_messages | boolean | Include full message history |
Request
curl -H "X-API-Key: YOUR_API_KEY" \
https://app.scrapely.co/api/v1/crm/conversationsResponse
{
"columns": {
"interested_reply": {
"conversations": [
{
"id": "conv-key",
"conversation_id": "123456-789012",
"account_handle": "myhandle",
"tag": "interested_reply",
"receiver": {
"screen_name": "lead_handle",
"name": "Interested Lead",
"bio": "CEO at...",
"followers_count": 10000
},
"crm": {
"notes": "Interested in demo",
"deal_value": 5000,
"deal_currency": "USD"
},
"last_message": {
"text": "Yes, I'd love to learn more!",
"time": "2024-01-15T14:30:00Z"
}
}
],
"count": 1
},
"negative_reply": {
"conversations": [],
"count": 0
}
},
"custom_tags": [
{"id": "uuid", "name": "Hot Lead", "color": "#ff0000", "key": "custom_uuid"}
],
"base_tags": [
"unread", "interested_reply", "negative_reply", "neutral_reply",
"engaged", "calendlyd", "booked", "not_interested", "not_qualified"
]
}Update CRM Data
/crm/updateUpdate notes, deal value, or tagsUpdate CRM data for a conversation including notes, deal value, and tags.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| conversation_id | string | Required | The conversation ID |
| account_handle | string | Required | The account handle |
| notes | string | Optional | Notes for the conversation |
| deal_value | number | Optional | Deal value |
| deal_currency | string | Optional | Currency code (default: USD) |
| tag | string | Optional | Tag to set (null to remove) |
Request
curl -X PATCH \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "123456-789012",
"account_handle": "myhandle",
"notes": "Interested in enterprise plan. Follow up Monday.",
"deal_value": 10000,
"deal_currency": "USD",
"tag": "interested_reply"
}' \
https://app.scrapely.co/api/v1/crm/updateResponse
{
"success": true,
"updated": {
"conversation_id": "123456-789012",
"account_handle": "myhandle",
"notes": "Interested in enterprise plan. Follow up Monday.",
"deal_value": 10000,
"deal_currency": "USD",
"tag": "interested_reply"
}
}Schedule Tweet
/scheduled-tweetsSchedule a tweet to be posted at a specific timeSchedule a tweet to be automatically posted from one of your accounts at a specified time. The tweet will be posted by the background worker when the scheduled time arrives.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| account_id | string | Required | The account UUID to post from |
| tweet_text | string | Required | The tweet content (max 280 characters) |
| scheduled_at | string | Required | ISO 8601 timestamp for when to post (must be in the future) |
Request
curl -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_id": "550e8400-e29b-41d4-a716-446655440000",
"tweet_text": "Just launched a new feature! Check it out",
"scheduled_at": "2024-03-20T15:00:00Z"
}' \
https://app.scrapely.co/api/v1/scheduled-tweetsResponse
{
"success": true,
"message": "Tweet scheduled successfully",
"scheduled_tweet": {
"id": "7f3a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"account_id": "550e8400-e29b-41d4-a716-446655440000",
"account_handle": "myhandle",
"tweet_text": "Just launched a new feature! Check it out",
"scheduled_at": "2024-03-20T15:00:00.000Z",
"status": "pending",
"created_at": "2024-03-15T10:30:00.000Z"
}
}pending- Waiting to be postedposted- Successfully postedfailed- Failed to post (check error_message)
List Scheduled Tweets
/scheduled-tweetsList all scheduled tweetsRetrieve a list of scheduled tweets for your accounts. Filter by account, status, or use pagination.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| account_id | string | Filter by specific account UUID |
| status | string | Filter by status: pending, posted, failed |
| limit | number | Max results (default: 50, max: 100) |
| offset | number | Pagination offset |
Request
curl -H "X-API-Key: YOUR_API_KEY" \
"https://app.scrapely.co/api/v1/scheduled-tweets?status=pending&limit=10"Response
{
"scheduled_tweets": [
{
"id": "7f3a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"account_id": "550e8400-e29b-41d4-a716-446655440000",
"account_handle": "myhandle",
"account_name": "My Account",
"account_image": "https://pbs.twimg.com/...",
"tweet_text": "Just launched a new feature!",
"scheduled_at": "2024-03-20T15:00:00.000Z",
"status": "pending",
"posted_at": null,
"error_message": null,
"created_at": "2024-03-15T10:30:00.000Z"
}
],
"pagination": {
"limit": 10,
"offset": 0,
"count": 1,
"total": 5
}
}Cancel Scheduled Tweet
/scheduled-tweetsCancel a pending scheduled tweetCancel a scheduled tweet before it gets posted. Only tweets with status "pending" can be cancelled.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| tweet_id | string | Required | The scheduled tweet UUID to cancel |
Request
curl -X DELETE \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tweet_id": "7f3a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c"
}' \
https://app.scrapely.co/api/v1/scheduled-tweetsResponse
{
"success": true,
"message": "Scheduled tweet cancelled",
"tweet_id": "7f3a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c"
}You can only cancel tweets with status "pending". Once a tweet has been posted or failed, it cannot be cancelled.
Error Handling
The API uses conventional HTTP response codes.
HTTP Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 400 | Bad Request - Invalid parameters |
| 401 | Unauthorized - Invalid API key |
| 403 | Forbidden - Subscription not active |
| 429 | Rate Limited - Too many requests |
| 500 | Internal Server Error |
Error Response Format
{
"error": "Unauthorized",
"message": "Invalid API key"
}Rate Limits
API requests are rate limited to ensure fair usage.
Response Headers
Every response includes these headers:
X-Request-ID: req_m1abc123def456
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1709234567Every response includes an X-Request-ID header. Include this ID when contacting support for faster debugging.
Rate Limit Error
{
"error": "Rate limited",
"message": "Too many requests. Please try again later.",
"retry_after": 45
}- Implement exponential backoff when retrying
- Cache responses when possible
- Use webhooks for real-time updates
Webhooks
Webhooks allow you to receive real-time notifications when events occur in your Scrapely account. Configure your webhook URL in Settings to start receiving events.
Webhook Events
| Event | Description |
|---|---|
| new_reply | A prospect replied to your DM |
| account_paused | A Twitter account was disconnected or paused |
| leads_exhausted (Deprecated) | A campaign has run out of leads to contact (Temporarily unavailable) |
Webhook Payload Format
All webhook payloads follow this structure:
{
"event": "event_type",
"timestamp": "2024-02-28T12:00:00.000Z",
"data": {
// Event-specific data
}
}new_reply Event
Sent when a prospect replies to your DM:
{
"event": "new_reply",
"timestamp": "2024-02-28T12:00:00.000Z",
"data": {
"conversation_id": "123456789-987654321",
"account_handle": "yourhandle",
"receiver": {
"screen_name": "prospect",
"name": "John Doe",
"profile_image_url": "https://pbs.twimg.com/..."
},
"message": {
"id": "1234567890123456789",
"text": "Hey! I'm interested, let's chat.",
"created_at": "2024-02-28T12:00:00.000Z"
}
}
}account_paused Event
Sent when a Twitter account is disconnected:
{
"event": "account_paused",
"timestamp": "2024-02-28T12:00:00.000Z",
"data": {
"account_id": "550e8400-e29b-41d4-a716-446655440000",
"account_handle": "yourhandle",
"reason": "Session expired or account disconnected"
}
}leads_exhausted Event (Deprecated)
This webhook is temporarily unavailable. The leads_exhausted webhook is being redesigned. Please check back later or contact support for updates.
Previously: Sent when a campaign runs out of leads:
{
"event": "leads_exhausted",
"timestamp": "2024-02-28T12:00:00.000Z",
"data": {
"campaign_id": "camp_abc123",
"campaign_name": "SaaS Founders Outreach",
"account_handle": "yourhandle",
"total_sent": 500
}
}Responding to Webhooks
Your endpoint should return a 200 status code within 30 seconds. If we don't receive a successful response, we'll retry up to 3 times with exponential backoff.
Validate the payload structure before processing to ensure it came from Scrapely.
MCP Server
The Scrapely MCP (Model Context Protocol) server lets AI assistants like Claude directly manage your Twitter/X DM outreach — campaigns, lead scraping, conversations, CRM, and scheduled tweets — all through natural language.
Requirements
| Requirement | Details |
|---|---|
| Node.js | v18 or higher |
| API Key | Generate from Settings |
| MCP Client | Claude Code, Claude Desktop, or any MCP-compatible client |
Installation
1. Clone or download the MCP server:
cd scrapely-mcp-server
npm install2. Set your API key:
export SCRAPELY_API_KEY=sk_live_your_key_hereConnect to Claude Code
Run this command to register the MCP server with Claude Code:
claude mcp add scrapely -- node /path/to/scrapely-mcp-server/server.jsConnect to Claude Desktop
Add this to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"scrapely": {
"command": "node",
"args": ["/path/to/scrapely-mcp-server/server.js"],
"env": {
"SCRAPELY_API_KEY": "sk_live_your_key_here"
}
}
}
}Test with MCP Inspector
The MCP Inspector opens a browser GUI where you can test every tool before connecting to Claude:
SCRAPELY_API_KEY=sk_live_your_key npm run inspectAvailable Tools
Once connected, the MCP server exposes 18 tools that map 1:1 to the Scrapely API:
| Tool | Description |
|---|---|
| list_accounts | List all connected Twitter/X accounts |
| add_account | Add a new account (cookies + proxy) |
| remove_account | Remove an account by ID or handle |
| update_cookies | Refresh cookies for an existing account |
| toggle_pause | Pause/unpause DM sending |
| toggle_followups_pause | Pause/unpause follow-up messages |
| create_scraping_source | Scrape followers/following with filters |
| check_scraping_status | Check progress of scraping jobs |
| launch_campaign | Launch a DM campaign with A/B testing |
| list_campaigns | List campaigns with stats |
| get_campaign_analytics | Reply rates, sentiment, A/B variant stats |
| fetch_conversations | Fetch conversations with message history |
| send_dm | Send a DM to an existing conversation |
| get_crm_data | Get conversations organized by CRM tags |
| update_crm | Update notes, deal value, and tags |
| schedule_tweet | Schedule a tweet for later |
| list_scheduled_tweets | List scheduled tweets with filters |
| cancel_scheduled_tweet | Cancel a pending scheduled tweet |
Example Prompts
Once connected to Claude, you can use natural language:
"Show me all my connected accounts"
"Scrape followers of @naval and @paulg, filter for founders with 1k+ followers"
"Launch a campaign called Q1 Outreach targeting my Tech Founders source"
"What are my campaign analytics? Which variant is performing best?"
"Show me all interested replies in the CRM"
"Send a followup to conversation 123456-789012"
"Schedule a tweet from my main account for tomorrow at 9am"
"Pause DM sending on my backup account"The MCP server translates natural language requests into Scrapely API calls. Every tool maps directly to an API endpoint documented above — same authentication, same payloads, same responses. Your API key is passed via the SCRAPELY_API_KEY environment variable.