Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

138 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Threads API Go Client

Go Reference Go Report Card CI codecov License: MIT Coverage

Unofficial, production-ready Go client for the Threads API with complete endpoint coverage, OAuth 2.0 authentication, rate limiting, and comprehensive error handling. Not affiliated with or endorsed by Meta.

Requires Go 1.21+. Tested on Go 1.21-1.24.

Features

  • Complete API coverage (posts, users, replies, insights, locations, search)
  • GIF attachments (GIPHY), ghost posts, and reply approvals
  • Fluent ContainerBuilder for advanced post creation
  • OAuth 2.0 flow and existing token support
  • Intelligent rate limiting with exponential backoff
  • Type-safe error handling with transient error detection
  • Thread-safe concurrent usage
  • Comprehensive test coverage

Installation

go get github.com/tirthpatell/threads-go

Quick Start

With Existing Token

client, err := threads.NewClientWithToken("your-access-token", &threads.Config{
    ClientID:     "your-client-id",
    ClientSecret: "your-client-secret",
    RedirectURI:  "your-redirect-uri",
})

// Create a post
post, err := client.CreateTextPost(context.Background(), &threads.TextPostContent{
    Text: "Hello Threads!",
})

OAuth Flow

config := &threads.Config{
    ClientID:     "your-client-id",
    ClientSecret: "your-client-secret", 
    RedirectURI:  "your-redirect-uri",
    Scopes:       []string{"threads_basic", "threads_content_publish"},
}

client, err := threads.NewClient(config)

// Get authorization URL. Persist state in the user's session (signed cookie,
// server-side session, etc.) — it MUST be compared against the state query
// parameter on the OAuth callback to prevent CSRF / authorization-code
// fixation (RFC 6749 §10.12, OAuth 2.0 Security BCP §4.7).
authURL, state, err := client.GetAuthURL(config.Scopes)
if err != nil {
    log.Fatal(err)
}
// Redirect user to authURL; on the callback, read ?code=... and ?state=...

// Exchange authorization code for token. The client refuses the exchange if
// the expected state does not match the state echoed on the callback
// (constant-time compare).
ctx := context.Background()
err = client.ExchangeCodeForToken(ctx, "auth-code-from-callback", state, receivedState)
err = client.GetLongLivedToken(ctx) // Convert to long-lived token

App Access Tokens

For APIs that require app-level auth instead of user tokens (e.g., oEmbed):

// Full API call (server-side only — exposes app secret)
tokenResp, err := client.GetAppAccessToken(ctx)

// Or use the shorthand format (no API call needed)
shorthand := client.GetAppAccessTokenShorthand() // "TH|<APP_ID>|<APP_SECRET>"

Environment Variables

export THREADS_CLIENT_ID="your-client-id"
export THREADS_CLIENT_SECRET="your-client-secret"
export THREADS_REDIRECT_URI="your-redirect-uri"
export THREADS_ACCESS_TOKEN="your-access-token"  # optional
client, err := threads.NewClientFromEnv()

Available Scopes

  • threads_basic - Basic profile access
  • threads_content_publish - Create and publish posts
  • threads_manage_insights - Access analytics data
  • threads_manage_replies - Manage replies and conversations
  • threads_read_replies - Read replies to posts
  • threads_keyword_search - Search functionality
  • threads_delete - Delete posts
  • threads_location_tagging - Location services

API Usage

Posts

// Create different post types
textPost, err := client.CreateTextPost(ctx, &threads.TextPostContent{
    Text: "Hello Threads!",
})

imagePost, err := client.CreateImagePost(ctx, &threads.ImagePostContent{
    Text: "Check this out!",
    ImageURL: "https://example.com/image.jpg",
})

// Get posts
post, err := client.GetPost(ctx, threads.PostID("123"))
posts, err := client.GetUserPosts(ctx, threads.UserID("456"), &threads.PaginationOptions{Limit: 25})

// Delete post
deletedID, err := client.DeletePost(ctx, threads.PostID("123"))

Users & Profiles

// Get user info
me, err := client.GetMe(ctx)
user, err := client.GetUser(ctx, threads.UserID("123"))

// Public profiles
publicUser, err := client.LookupPublicProfile(ctx, "@username")
posts, err := client.GetPublicProfilePosts(ctx, "username", nil)

Replies & Conversations

// Reply to posts
reply, err := client.ReplyToPost(ctx, threads.PostID("123"), &threads.PostContent{
    Text: "Great post!",
})

// Get replies
replies, err := client.GetReplies(ctx, threads.PostID("123"), &threads.RepliesOptions{Limit: 50})

// Manage visibility
err = client.HideReply(ctx, threads.PostID("456"))

Insights & Analytics

// Post insights
insights, err := client.GetPostInsights(ctx, threads.PostID("123"), []string{"views", "likes"})

// Account insights  
insights, err := client.GetAccountInsights(ctx, threads.UserID("456"), []string{"views"}, "lifetime")

Search & Locations

// Search posts by keyword
results, err := client.KeywordSearch(ctx, "golang", &threads.SearchOptions{Limit: 25})

// Search posts filtered by media type
imageResults, err := client.KeywordSearch(ctx, "nature", &threads.SearchOptions{
    MediaType: threads.MediaTypeImage,  // Filter for IMAGE posts only (TEXT, IMAGE, or VIDEO)
    Limit: 25,
})

// Search posts by topic tag
tagResults, err := client.KeywordSearch(ctx, "#technology", &threads.SearchOptions{
    SearchMode: threads.SearchModeTag,
    Limit: 25,
})

// Location search
locations, err := client.SearchLocations(ctx, "New York", nil, nil)

GIF Posts

// Create a post with a GIF attachment (GIPHY)
gifPost, err := client.CreateTextPost(ctx, &threads.TextPostContent{
    Text: "Check out this GIF!",
    GIFAttachment: &threads.GIFAttachment{
        GIFID:    "giphy-gif-id",
        Provider: threads.GIFProviderGiphy,
    },
})

Note: Tenor support is deprecated and will be sunset on March 31, 2026. Use GIPHY instead.

Ghost Posts

// Create a ghost post (auto-expiring)
ghost, err := client.CreateTextPost(ctx, &threads.TextPostContent{
    Text:        "This will disappear!",
    IsGhostPost: true,
})

// Retrieve a user's ghost posts
ghosts, err := client.GetUserGhostPosts(ctx, userID, nil)

Reply Approvals

// Approve or ignore pending replies
err = client.ApprovePendingReply(ctx, threads.PostID("reply-id"))
err = client.IgnorePendingReply(ctx, threads.PostID("reply-id"))

Container Builder

For advanced post creation, use the fluent ContainerBuilder:

builder := threads.NewContainerBuilder().
    SetMediaType("TEXT").
    SetText("Hello from the builder!").
    SetReplyControl(threads.ReplyControlFollowersOnly).
    SetTopicTag("golang").
    SetLocationID("location-id")
params := builder.Build()

Pagination & Iterators

For large datasets, use iterators to automatically handle pagination:

// Posts iterator
userID := threads.ConvertToUserID("user_id")
iterator := threads.NewPostIterator(client, userID, &threads.PostsOptions{
    Limit: 25,
})

for iterator.HasNext() {
    response, err := iterator.Next(ctx)
    if err != nil {
        log.Fatal(err)
    }
    
    for _, post := range response.Data {
        fmt.Printf("Post: %s\n", post.Text)
    }
}

// Replies iterator
replyIterator := threads.NewReplyIterator(client, threads.PostID("123"), &threads.RepliesOptions{
    Limit: 50,
})

// Search iterator
searchIterator := threads.NewSearchIterator(client, "golang", "keyword", &threads.SearchOptions{
    Limit: 25,
})

// Collect all results at once
allPosts, err := iterator.Collect(ctx)

Configuration

config := &threads.Config{
    ClientID:     "your-client-id",
    ClientSecret: "your-client-secret",
    RedirectURI:  "https://yourapp.com/callback",
    Scopes:       []string{"threads_basic", "threads_content_publish"},
    HTTPTimeout:  30 * time.Second,
    Debug:        false,
}

For advanced configuration including retry logic, custom logging, and token storage, see the GoDoc documentation.

Error Handling

The client provides typed errors for different scenarios:

switch {
case threads.IsAuthenticationError(err):
    // Handle authentication issues
case threads.IsRateLimitError(err):
    rateLimitErr := err.(*threads.RateLimitError)
    time.Sleep(rateLimitErr.RetryAfter)
case threads.IsValidationError(err):
    validationErr := err.(*threads.ValidationError)
    log.Printf("Invalid %s: %s", validationErr.Field, err.Error())
case threads.IsTransientError(err):
    // Safe to retry — transient API error
}

Error types: AuthenticationError, RateLimitError, ValidationError, NetworkError, APIError

Testing

# Unit tests
go test ./...

# Integration tests (requires valid credentials)
export THREADS_ACCESS_TOKEN="your-token"
go test ./tests/integration/...

Contributing

See CONTRIBUTING.md for guidelines.

Official Documentation

License

MIT License - see LICENSE file for details.

About

Go client for Meta's Threads - OAuth 2.0, rate limiting, full API coverage, production-ready

Topics

Resources

Contributing

Stars

9 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages