Go SDK
The voxburst-go package is the official Go client for the VoxBurst API. It provides context support, automatic retries, typed errors, and pagination helpers.
go get github.com/FortheanLabsProjects/voxburst-goRequires Go 1.21 or later.
Official package. github.com/FortheanLabsProjects/voxburst-go is the official VoxBurst Go client, maintained by the VoxBurst team under the FortheanLabsProjects GitHub organization. The module path reflects the organization name, not a third-party fork.
What do you want to do?
- Create your first post
- Post a media-backed Instagram post
- Post to multiple platforms at once
- Schedule a post
- Upload media and wait for readiness
- Fetch analytics
- Handle API errors
- Paginate through posts
Build your first Go integration
A guided path from go get to a published post.
package main
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/FortheanLabsProjects/voxburst-go/voxburst"
)
func main() {
// 1. Create the client
client := voxburst.NewClient(os.Getenv("VOXBURST_API_KEY"))
ctx := context.Background()
// 2. Verify auth — list connected accounts
accounts, err := client.Accounts.List(ctx, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Connected accounts: %d\n", len(accounts.Data))
for _, a := range accounts.Data {
fmt.Printf(" %s %s (%s)\n", a.ID, a.Username, a.Platform)
}
// 3. Find the account to post to
var twitterID string
for _, a := range accounts.Data {
if string(a.Platform) == "twitter" {
twitterID = a.ID
break
}
}
// 4. Create a draft
draft, err := client.Posts.CreateDraft(ctx,
"Hello from VoxBurst Go SDK! 🚀",
[]string{twitterID},
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Draft: %s (status: %s)\n", draft.ID, draft.Status)
// Draft: post_abc123 (status: draft)
// 5. Schedule it
scheduledTime := time.Now().Add(24 * time.Hour)
scheduled, err := client.Posts.Schedule(ctx,
"Scheduled post from Go!",
[]string{twitterID},
scheduledTime,
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Scheduled for: %s\n", scheduled.ScheduledFor)
// 6. Inspect result
result, err := client.Posts.Get(ctx, draft.ID)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Status: %s\n", result.Status)
for _, p := range result.Platforms {
fmt.Printf(" %s: %s — %s\n", p.Platform, p.Status, p.PlatformPostURL)
}
// 7. Publish immediately
published, err := client.Posts.Publish(ctx, draft.ID)
if err != nil {
if voxburst.IsRateLimited(err) {
log.Printf("Rate limited — wait and retry")
} else {
log.Fatal(err)
}
}
fmt.Printf("Published: %s\n", published.ID)
}Configuration
client := voxburst.NewClient(apiKey,
voxburst.WithBaseURL("https://api.voxburst.io/v1"),
voxburst.WithTimeout(30 * time.Second),
voxburst.WithHTTPClient(&http.Client{Timeout: 60 * time.Second}),
)| Option | Description |
|---|---|
WithBaseURL(url) | Override API base URL (e.g. for staging) |
WithTimeout(d) | Request timeout — increase for large video uploads |
WithHTTPClient(c) | Provide a custom *http.Client |
Complete Instagram example
Instagram requires ContentType and at least one media item. The full flow: upload → poll until READY → create post.
// Step 1: upload media
mediaID, err := uploadMedia(ctx, client, "photo.jpg")
if err != nil { log.Fatal(err) }
// Step 2: find Instagram account
accounts, _ := client.Accounts.List(ctx, nil)
var igID string
for _, a := range accounts.Data {
if string(a.Platform) == "instagram" { igID = a.ID; break }
}
// Step 3: create the post
post, err := client.Posts.Create(ctx, voxburst.CreatePostParams{
Content: "Behind the scenes 📸",
AccountIds: []string{igID},
ContentType: "IMAGE",
Media: []string{mediaID},
FirstComment: "#photography #launch",
FirstCommentDelay: 30,
})
if err != nil { log.Fatal(err) }
fmt.Printf("Created: %s (status: %s)\n", post.ID, post.Status)
// Step 4: check result
result, _ := client.Posts.Get(ctx, post.ID)
for _, p := range result.Platforms {
fmt.Printf("%s: %s — %s\n", p.Platform, p.Status, p.PlatformPostURL)
}Where uploadMedia implements the presigned-URL flow — see Media upload below.
Carousel
post, err := client.Posts.Create(ctx, voxburst.CreatePostParams{
Content: "Swipe to see more →",
AccountIds: []string{igID},
ContentType: "CAROUSEL",
Media: []string{"media_slide1", "media_slide2", "media_slide3"},
})Cross-platform example
Post the same content to multiple platforms with per-platform overrides.
// Collect account IDs for multiple platforms
accounts, _ := client.Accounts.List(ctx, nil)
var accountIDs []string
for _, a := range accounts.Data {
switch string(a.Platform) {
case "instagram", "twitter", "linkedin":
accountIDs = append(accountIDs, a.ID)
}
}
post, err := client.Posts.Create(ctx, voxburst.CreatePostParams{
Content: "Announcing VoxBurst 2.0 — unified social scheduling.",
AccountIds: accountIDs,
ContentType: "IMAGE",
Media: []string{"media_launch_image"},
PlatformOverrides: map[voxburst.Platform]any{
"TWITTER": map[string]any{
"content": "VoxBurst 2.0 just shipped 🚀 #ProductLaunch",
},
"LINKEDIN": map[string]any{
"content": "Today we are shipping VoxBurst 2.0. Built for teams that publish across every platform at scale.",
},
},
FirstComment: "#VoxBurst20 #Launch",
FirstCommentDelay: 60,
})
if err != nil { log.Fatal(err) }
// After publishing, check each platform
result, _ := client.Posts.Get(ctx, post.ID)
for _, p := range result.Platforms {
fmt.Printf("%s: %s — %s\n", p.Platform, p.Status, p.PlatformPostURL)
}
// Retry any failures
if result.Status == "partial" || result.Status == "failed" {
retried, _ := client.Posts.Retry(ctx, post.ID)
fmt.Printf("Retried %d platforms\n", len(retried.Platforms))
}Schedule a post
scheduledTime := time.Date(2026, 6, 1, 15, 0, 0, 0, time.UTC)
post, err := client.Posts.Schedule(ctx,
"Launch day 🚀",
[]string{"acc_twitter_abc"},
scheduledTime,
)
// post.Status == "scheduled"
// post.ScheduledFor.Equal(scheduledTime)Post status lifecycle
draft → scheduled → publishing → published
↓ ↓
failed partial
↓ ↓
Retry() Retry()| Status | Update | Retry | Delete | Publish |
|---|---|---|---|---|
draft | ✅ | ✗ | ✅ | ✅ |
scheduled | ✅ | ✗ | ✅ | ✅ |
publishing | ✗ | ✗ | ✗ | ✗ |
published | ✗ | ✗ | ✗ | ✗ |
failed | ✅ | ✅ | ✅ | ✗ |
partial | ✅ | ✅ | ✗ | ✗ |
Media upload
func uploadMedia(ctx context.Context, client *voxburst.Client, filename string) (string, error) {
data, err := os.ReadFile(filename)
if err != nil { return "", err }
// Request presigned URL
upload, err := client.Media.RequestUploadURL(ctx, voxburst.RequestUploadParams{
Filename: filepath.Base(filename),
ContentType: "image/jpeg",
SizeBytes: int64(len(data)),
})
if err != nil { return "", err }
// PUT bytes directly to S3
req, _ := http.NewRequestWithContext(ctx, http.MethodPut, upload.UploadURL, bytes.NewReader(data))
req.Header.Set("Content-Type", "image/jpeg")
resp, err := http.DefaultClient.Do(req)
if err != nil || resp.StatusCode != 200 {
return "", fmt.Errorf("upload failed: status %d", resp.StatusCode)
}
// Poll until READY
for {
media, err := client.Media.Get(ctx, upload.MediaID)
if err != nil { return "", err }
switch media.Status {
case voxburst.MediaStatusReady:
return upload.MediaID, nil
case voxburst.MediaStatusFailed:
return "", fmt.Errorf("media processing failed")
}
time.Sleep(2 * time.Second)
}
}Key response fields from Media.Get:
media.ID— the media ID to pass asMedia: []string{mediaID}in postsmedia.Status—MediaStatusPending,MediaStatusProcessing,MediaStatusReady,MediaStatusFailedmedia.ContentType—"image/jpeg"(VoxBurst normalizes PNG/WebP to JPEG)media.PublicURL— CDN URL of the file after processing
Analytics (REST-only)
voxburst-go does not wrap analytics endpoints — use net/http directly. All 21 analytics routes follow the same pattern. See the Analytics API reference for the full endpoint list, query parameters, and response shapes.
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
func fetchAnalytics(path string) (map[string]any, error) {
req, _ := http.NewRequest("GET", "https://api.voxburst.io"+path, nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("VOXBURST_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
return result, nil
}
// Post-level metrics
postMetrics, _ := fetchAnalytics("/v1/analytics/posts/post_abc123?startDate=2026-05-01&endDate=2026-05-31")
// Account-level metrics
acctMetrics, _ := fetchAnalytics("/v1/analytics/accounts/acc_123?startDate=2026-05-01&endDate=2026-05-31")
// AI Insights (paid plans — requires system AI provider config)
insights, _ := fetchAnalytics("/v1/analytics/insights")
// Hashtag performance
hashtags, _ := fetchAnalytics("/v1/analytics/hashtag-performance?startDate=2026-05-01&endDate=2026-05-31&limit=10")
_ = fmt.Sprintf("%v %v %v %v", postMetrics, acctMetrics, insights, hashtags)Error handling
import "github.com/FortheanLabsProjects/voxburst-go/voxburst"
_, err := client.Posts.Get(ctx, "post_notfound")
if err != nil {
switch {
case voxburst.IsNotFound(err):
log.Println("post not found")
case voxburst.IsUnauthorized(err):
log.Println("invalid API key — check VOXBURST_API_KEY")
case voxburst.IsRateLimited(err):
log.Println("rate limited — back off and retry")
time.Sleep(60 * time.Second)
case voxburst.IsServerError(err):
log.Println("VoxBurst server error — transient, retry")
default:
log.Fatalf("unexpected error: %v", err)
}
}| Helper | HTTP status | When to use |
|---|---|---|
voxburst.IsNotFound(err) | 404 | Resource does not exist |
voxburst.IsUnauthorized(err) | 401 | Invalid or expired API key |
voxburst.IsRateLimited(err) | 429 | Too many requests — back off |
voxburst.IsServerError(err) | 5xx | Transient server error — retry |
Retry vs recreate:
| Situation | Action |
|---|---|
| 429 rate limit | Wait and retry the same call |
| 5xx server error | Retry up to 3 times with backoff |
| 401 unauthorized | Fix the API key — do not retry |
Post status failed | Call client.Posts.Retry(ctx, id) |
Post status partial | Call client.Posts.Retry(ctx, id) |
| Media processing failed | Re-upload with a new file |
Pagination
// List() — one page at a time
resp, err := client.Posts.List(ctx, &voxburst.ListPostsParams{
Status: voxburst.PostStatusScheduled,
Limit: 20,
})
// resp.Data — current page
// resp.Pagination.HasMore — more pages available
// resp.Pagination.NextCursor — pass as Cursor on the next call
if resp.Pagination.HasMore {
next, _ := client.Posts.List(ctx, &voxburst.ListPostsParams{
Cursor: resp.Pagination.NextCursor,
})
}
// ListAll() — auto-paginates; use for processing everything
iter := client.Posts.ListAll(ctx, &voxburst.ListPostsParams{Limit: 50})
for iter.Next() {
p := iter.Post()
fmt.Printf("%s: %s\n", p.ID, p.Status)
}
if err := iter.Err(); err != nil {
log.Fatal(err)
}When to use what:
List()— when you need one page or want to checkHasMorebefore continuingListAll()— when you need every result (export, sync, analytics pipeline)
Next steps
- API Reference — full REST endpoint documentation
- Getting Started — first API calls and environment setup
- Platform guides — platform-specific posting examples
- Working with local files — media upload decision guide
- Common tasks — task-oriented recipes