threads-api-ai
Version:
A Node.js client library for the Facebook Threads API with TypeScript support
613 lines (439 loc) • 14.7 kB
Markdown
# threads-api-ai
A Node.js client library for the Facebook Threads API with full TypeScript support.
## Features
- ✅ **OAuth 2.0 Authentication** - Complete authentication flow with short-lived and long-lived tokens
- ✅ **User Profile API** - Get user profile information and threads
- ✅ **Publishing API** - Create and publish text, image, video, and carousel posts
- ✅ **TypeScript Support** - Full type definitions for all API methods
- ✅ **Promise-based** - Modern async/await API
- ✅ **Error Handling** - Comprehensive error handling with detailed messages
- 🚧 **More features coming soon** - Insights, replies, webhooks, and more
## Installation
```bash
npm install threads-api-ai
```
## Prerequisites
Before using this library, you need to:
1. Create a Threads app in the [Meta Developer Portal](https://developers.facebook.com/)
2. Get your App ID and App Secret
3. Configure OAuth redirect URI in your app settings
4. Request necessary permissions/scopes
## Quick Start
```typescript
import ThreadsAPI from 'threads-api-ai';
// Initialize the client
const client = new ThreadsAPI({
appId: 'YOUR_APP_ID',
appSecret: 'YOUR_APP_SECRET',
redirectUri: 'https://yourapp.com/callback'
});
// Step 1: Generate authorization URL
const authUrl = client.auth.getAuthorizationUrl([
'threads_basic',
'threads_content_publish'
], 'optional-state-for-csrf');
console.log('Visit this URL to authorize:', authUrl);
// Step 2: After user authorizes, exchange code for tokens
const code = 'CODE_FROM_CALLBACK';
const tokens = await client.auth.completeAuthFlow(code);
console.log('Access Token:', tokens.access_token);
console.log('Expires in:', tokens.expires_in, 'seconds');
```
## Authentication Guide
The Threads API uses OAuth 2.0 for authentication. This library provides methods for the complete authentication flow.
### Available Scopes
- `threads_basic` - Read basic profile information
- `threads_content_publish` - Publish content on behalf of the user
- `threads_manage_insights` - Read insights and analytics
- `threads_manage_replies` - Manage replies to posts
- `threads_read_replies` - Read replies to posts
### Complete Authentication Flow
```typescript
import ThreadsAPI from 'threads-api-ai';
const client = new ThreadsAPI({
appId: process.env.THREADS_APP_ID!,
appSecret: process.env.THREADS_APP_SECRET!,
redirectUri: 'https://yourapp.com/auth/callback'
});
// 1. Generate authorization URL
const authUrl = client.auth.getAuthorizationUrl([
'threads_basic',
'threads_content_publish'
], 'your-random-state-string');
// Redirect user to authUrl...
// User authorizes and is redirected back to your callback URL with a code
// 2. Exchange code for tokens (this method handles both short and long-lived tokens)
const tokens = await client.auth.completeAuthFlow(code);
// Save tokens.access_token securely - it's valid for ~60 days
```
### Manual Token Exchange
If you prefer to handle the token exchange manually:
```typescript
// Get short-lived token (valid for ~1 hour)
const shortToken = await client.auth.getAccessToken(code);
// Exchange for long-lived token (valid for ~60 days)
const longToken = await client.auth.getLongLivedToken(shortToken.access_token);
```
### Refreshing Long-Lived Tokens
Long-lived tokens expire after ~60 days. Refresh them before they expire:
```typescript
const refreshedToken = await client.auth.refreshLongLivedToken(
currentLongLivedToken
);
// Save the new token - it's valid for another ~60 days
```
## Express.js Example
Here's a complete example using Express.js:
```typescript
import express from 'express';
import ThreadsAPI from 'threads-api-ai';
const app = express();
const client = new ThreadsAPI({
appId: process.env.THREADS_APP_ID!,
appSecret: process.env.THREADS_APP_SECRET!,
redirectUri: 'http://localhost:3000/auth/callback'
});
// Route to initiate OAuth
app.get('/auth/threads', (req, res) => {
const authUrl = client.auth.getAuthorizationUrl([
'threads_basic',
'threads_content_publish'
], req.session.state); // Use session-based state for security
res.redirect(authUrl);
});
// OAuth callback route
app.get('/auth/callback', async (req, res) => {
try {
const { code, state } = req.query;
// Verify state matches (CSRF protection)
if (state !== req.session.state) {
throw new Error('State mismatch');
}
// Complete auth flow and get long-lived token
const tokens = await client.auth.completeAuthFlow(code as string);
// Save tokens securely (e.g., in database)
req.session.accessToken = tokens.access_token;
res.send('Authentication successful!');
} catch (error) {
console.error('Auth error:', error);
res.status(500).send('Authentication failed');
}
});
app.listen(3000);
```
## User Profile API
Once you have an access token, you can retrieve user profile information and threads.
### Get User Profile
```typescript
// Get the authenticated user's profile
const profile = await client.profile.getProfile(accessToken);
console.log('User ID:', profile.id);
console.log('Username:', profile.username);
console.log('Profile Picture:', profile.threads_profile_picture_url);
console.log('Bio:', profile.threads_biography);
// Get specific fields only
const customProfile = await client.profile.getProfile(
accessToken,
['id', 'username'] // Only fetch these fields
);
```
### Get User's Threads
```typescript
// Get the authenticated user's threads
const threads = await client.profile.getUserThreads(accessToken);
console.log('User threads:', threads.data);
// Get threads with custom fields and limit
const customThreads = await client.profile.getUserThreads(
accessToken,
'me', // or specific user ID
['id', 'text', 'permalink', 'timestamp'], // fields to retrieve
10 // limit
);
```
### Get Single Thread
```typescript
// Get a specific thread by ID
const thread = await client.profile.getThread(accessToken, 'THREAD_ID');
console.log('Thread text:', thread.text);
console.log('Permalink:', thread.permalink);
console.log('Media URL:', thread.media_url);
```
## Publishing API
Create and publish text posts, images, videos, and carousels.
### Publish a Text Post
```typescript
// Simple text post (all in one step)
const result = await client.publishing.createAndPublishThread(
accessToken,
'me',
{
media_type: 'TEXT',
text: 'Hello from Threads API! 👋'
}
);
console.log('Published thread ID:', result.id);
```
### Publish an Image Post
```typescript
// Post with image
const imagePost = await client.publishing.createAndPublishThread(
accessToken,
'me',
{
media_type: 'IMAGE',
image_url: 'https://example.com/image.jpg',
text: 'Check out this image!'
}
);
```
### Publish a Video Post
```typescript
// Post with video
const videoPost = await client.publishing.createAndPublishThread(
accessToken,
'me',
{
media_type: 'VIDEO',
video_url: 'https://example.com/video.mp4',
text: 'Watch this video!'
}
);
```
### Publish a Carousel Post
```typescript
// Create carousel items first
const item1 = await client.publishing.createCarouselItem(
accessToken,
'me',
{ image_url: 'https://example.com/image1.jpg' }
);
const item2 = await client.publishing.createCarouselItem(
accessToken,
'me',
{ image_url: 'https://example.com/image2.jpg' }
);
// Create and publish carousel
const carousel = await client.publishing.createAndPublishThread(
accessToken,
'me',
{
media_type: 'CAROUSEL',
carousel_item_ids: [item1.id, item2.id],
text: 'Swipe to see more!'
}
);
```
### Reply to a Thread
```typescript
// Reply to another thread
const reply = await client.publishing.createAndPublishThread(
accessToken,
'me',
{
media_type: 'TEXT',
text: 'This is a reply!',
reply_to_id: 'PARENT_THREAD_ID'
}
);
```
### Control Who Can Reply
```typescript
// Control reply permissions
const post = await client.publishing.createAndPublishThread(
accessToken,
'me',
{
media_type: 'TEXT',
text: 'Only people I follow can reply',
reply_control: 'accounts_you_follow' // or 'everyone' or 'mentioned_only'
}
);
```
### Manual Two-Step Publishing
If you need more control, you can manually create and publish in two steps:
```typescript
// Step 1: Create thread container
const container = await client.publishing.createThread(
accessToken,
'me',
{
media_type: 'TEXT',
text: 'My post'
}
);
// Step 2: Publish the container
const published = await client.publishing.publishThread(
accessToken,
'me',
{
creation_id: container.id
}
);
console.log('Published thread ID:', published.id);
```
### Check Publishing Limits
```typescript
// Get publishing rate limit info
const limits = await client.publishing.getPublishingLimit(accessToken);
console.log('Quota usage:', limits.data);
```
## Error Handling
The library throws detailed errors with API error codes and messages:
```typescript
try {
const tokens = await client.auth.getAccessToken(code);
} catch (error) {
console.error(error.message);
// Example: "Threads API Error (190): Invalid OAuth access token"
}
```
## API Reference
### `ThreadsAPI`
Main client class.
#### Constructor
```typescript
new ThreadsAPI(config: ThreadsAPIConfig)
```
**Parameters:**
- `config.appId` (string) - Your Threads App ID
- `config.appSecret` (string) - Your Threads App Secret
- `config.redirectUri` (string) - OAuth redirect URI
- `config.apiVersion` (string, optional) - API version (default: 'v1.0')
#### Properties
- `auth` - Authentication handler
- `profile` - User profile handler
- `publishing` - Publishing handler
### `ThreadsAuth`
Authentication handler (accessible via `client.auth`).
#### Methods
##### `getAuthorizationUrl(scopes, state?)`
Generate OAuth authorization URL.
**Parameters:**
- `scopes` (ThreadsScope[]) - Array of permission scopes
- `state` (string, optional) - State parameter for CSRF protection
**Returns:** Authorization URL (string)
##### `getAccessToken(code)`
Exchange authorization code for short-lived access token.
**Parameters:**
- `code` (string) - Authorization code from callback
**Returns:** Promise<AccessTokenResponse>
##### `getLongLivedToken(shortLivedToken)`
Exchange short-lived token for long-lived token.
**Parameters:**
- `shortLivedToken` (string) - Short-lived access token
**Returns:** Promise<LongLivedTokenResponse>
##### `refreshLongLivedToken(longLivedToken)`
Refresh a long-lived token.
**Parameters:**
- `longLivedToken` (string) - Current long-lived token
**Returns:** Promise<RefreshTokenResponse>
##### `completeAuthFlow(code)`
Complete full OAuth flow (recommended method).
**Parameters:**
- `code` (string) - Authorization code from callback
**Returns:** Promise<LongLivedTokenResponse>
### `ThreadsProfile`
User profile handler (accessible via `client.profile`).
#### Methods
##### `getProfile(accessToken, fields?)`
Get the authenticated user's profile.
**Parameters:**
- `accessToken` (string) - User's access token
- `fields` (string[], optional) - Fields to retrieve (default: id, username, threads_profile_picture_url, threads_biography)
**Returns:** Promise<ThreadsUserProfile>
##### `getUserThreads(accessToken, userId?, fields?, limit?)`
Get a user's threads.
**Parameters:**
- `accessToken` (string) - User's access token
- `userId` (string, optional) - User ID (default: 'me')
- `fields` (string[], optional) - Fields to retrieve
- `limit` (number, optional) - Number of threads (default: 25, max: 100)
**Returns:** Promise<any>
##### `getThread(accessToken, threadId, fields?)`
Get a specific thread by ID.
**Parameters:**
- `accessToken` (string) - User's access token
- `threadId` (string) - Thread ID
- `fields` (string[], optional) - Fields to retrieve
**Returns:** Promise<any>
### `ThreadsPublishing`
Publishing handler (accessible via `client.publishing`).
#### Methods
##### `createThread(accessToken, userId, params)`
Create a thread container (step 1 of publishing).
**Parameters:**
- `accessToken` (string) - User's access token
- `userId` (string) - User ID (default: 'me')
- `params` (CreateThreadParams) - Thread creation parameters
**Returns:** Promise<CreateThreadResponse>
##### `publishThread(accessToken, userId, params)`
Publish a thread container (step 2 of publishing).
**Parameters:**
- `accessToken` (string) - User's access token
- `userId` (string) - User ID (default: 'me')
- `params` (PublishThreadParams) - Publishing parameters
**Returns:** Promise<PublishThreadResponse>
##### `createAndPublishThread(accessToken, userId, params)`
Create and publish a thread in one step (convenience method).
**Parameters:**
- `accessToken` (string) - User's access token
- `userId` (string) - User ID (default: 'me')
- `params` (CreateThreadParams) - Thread creation parameters
**Returns:** Promise<PublishThreadResponse>
##### `createCarouselItem(accessToken, userId, params)`
Create a carousel item for carousel posts.
**Parameters:**
- `accessToken` (string) - User's access token
- `userId` (string) - User ID (default: 'me')
- `params` (CreateCarouselItemParams) - Carousel item parameters
**Returns:** Promise<CreateThreadResponse>
##### `getPublishingLimit(accessToken, userId?)`
Get the publishing limit for the user.
**Parameters:**
- `accessToken` (string) - User's access token
- `userId` (string, optional) - User ID (default: 'me')
**Returns:** Promise<any>
## TypeScript Types
All types are exported and available for use:
```typescript
import {
ThreadsAPIConfig,
ThreadsScope,
AccessTokenResponse,
LongLivedTokenResponse,
RefreshTokenResponse,
ThreadsUserProfile,
MediaType,
CreateThreadParams,
CreateThreadResponse,
PublishThreadParams,
PublishThreadResponse,
CreateCarouselItemParams,
Thread
} from 'threads-api-ai';
```
## Roadmap
- [x] OAuth 2.0 Authentication
- [x] User Profile API
- [x] Publishing API (create posts)
- [x] Text posts
- [x] Image posts
- [x] Video posts
- [x] Carousel posts
- [x] Reply control
- [x] Publishing limits
- [ ] Media API (upload images/videos)
- [ ] Insights API (analytics)
- [ ] Replies API (manage comments)
- [ ] Webhooks support
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
MIT
## Support
For issues and questions, please use the [GitHub Issues](https://github.com/yourusername/threads-api-ai/issues) page.
## Disclaimer
This is an unofficial library and is not affiliated with Meta Platforms, Inc.
## Resources
- [Official Threads API Documentation](https://developers.facebook.com/docs/threads)
- [Meta Developer Portal](https://developers.facebook.com/)