Twitter API in Go (Golang): working code
To get Twitter/X data in Go, send HTTPS requests with net/http, decode the JSON into typed structs and follow the cursor page by page. X ships no official Go SDK, and the official X API bills $0.005 per post read.
Short answer: X publishes official SDKs only for Python and TypeScript, so Go code calls the official X API through net/http or a community library such as gotwi, with a developer account and prepaid credits: $0.005 per post. For public data, use Sorsa API from net/http: one key, 20 tweets per search request from $0.07 per 1,000; 100 free requests cover 2,000.
- Endpoint
POST /search-tweets- Page size
- 20 tweets per request
- Price
- from $0.07 per 1,000 tweets
#Does X publish an official Go SDK?
No. X's developer documentation lists official SDKs for Python and TypeScript only and files Go under community libraries that X doesn't maintain (X's tools and libraries page (opens in a new tab)). X's one official Go project is xurl, a command-line tool you run in a terminal, not a package you import.
That leaves the community clients, and only one of them still ships releases. The dates below come from each project's GitHub history:
| Go client | Latest release | State on November 2, 2026 |
|---|---|---|
| gotwi | v0.18.5, August 21, 2026 | Maintained; covers most v2 endpoints with OAuth 1.0a, a bearer token or PKCE |
| go-twitter v2 (g8rswimmer) | v2.1.5, December 29, 2022 | No release in almost four years |
| go-twitter (dghubble) | archived in 2022 | Written for the retired v1.1 API |
gotwi
- Latest release
- v0.18.5, August 21, 2026
- State on November 2, 2026
- Maintained; covers most v2 endpoints with OAuth 1.0a, a bearer token or PKCE
go-twitter v2 (g8rswimmer)
- Latest release
- v2.1.5, December 29, 2022
- State on November 2, 2026
- No release in almost four years
go-twitter (dghubble)
- Latest release
- archived in 2022
- State on November 2, 2026
- Written for the retired v1.1 API
A library only shapes the requests. Every call still spends X's prepaid credits: $0.005 per post and $0.010 per user, up to 3M post reads a month (X's price table (opens in a new tab)). From November 22, 2026 the authors and media that come back through expansions are billed at the same rates (X's notice of October 23 (opens in a new tab)), so the search most Go code sends costs up to three times the post price:
q := url.Values{
"query": {"golang lang:en"},
"max_results": {"100"},
"expansions": {"author_id"},
}
req, _ := http.NewRequestWithContext(ctx, http.MethodGet,
"https://api.x.com/2/tweets/search/recent?"+q.Encode(), nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("X_BEARER_TOKEN"))
// 100 posts: $0.50. From Nov 22, 2026, with authors: up to $1.50
What the official route needs before the first call
- A developer account at console.x.com, opened with an X account, with X's Developer Agreement accepted.
- An app, and its bearer token, which X shows only once.
- Credits bought in advance and spent on every post and user a response returns.
- OAuth user tokens for anything that posts, likes or follows.
Reading public posts, profiles and followers needs none of these. The same choice for Tweepy and xdk is laid out in the Twitter API in Python guide; the rest of this page does the job in Go.
#Get tweets in Go with one API key
To read X data from Go, use Sorsa API, a Twitter (X) API and scraper: one ApiKey header instead of OAuth, 20 requests per second on every plan, free accounts included, search back to 2006, and search results from $0.07 per 1,000 tweets with the author's full profile inside each one. The standard library is the whole client: net/http sends the request and encoding/json reads the reply, so go.mod gains no dependency. Sorsa API never asks for your X login, so your X account carries no risk.
-
Get a key. Sign up and copy the API key from the dashboard. The first 100 requests are free, need no card and never expire.
-
Start a module and store the key. Keep the key in an environment variable and call the API from server-side code only, never from a browser or an app you hand to users. The code in this guide needs Go 1.25 or newer.
Terminal go mod init example.com/xdata export SORSA_API_KEY="your_key" # PowerShell: $env:SORSA_API_KEY="your_key" -
Search for tweets. Send a query in X's search syntax to
POST /search-tweets(opens in a new tab) with the key in theApiKeyheader, then rungo run ..package main import ( "bytes" "encoding/json" "fmt" "log" "net/http" "os" "time" ) func main() { key := os.Getenv("SORSA_API_KEY") // or "YOUR_API_KEY" to try it body, _ := json.Marshal(map[string]string{ "query": "golang lang:en", "order": "latest", }) req, err := http.NewRequest(http.MethodPost, "https://api.sorsa.io/v3/search-tweets", bytes.NewReader(body)) if err != nil { log.Fatal(err) } req.Header.Set("ApiKey", key) req.Header.Set("Content-Type", "application/json") client := &http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { log.Fatalf("sorsa: %s", resp.Status) } var page struct { Tweets []struct { ID string `json:"id"` FullText string `json:"full_text"` User struct { Username string `json:"username"` } `json:"user"` } `json:"tweets"` NextCursor string `json:"next_cursor"` } if err := json.NewDecoder(resp.Body).Decode(&page); err != nil { log.Fatal(err) } for _, t := range page.Tweets { fmt.Println(t.User.Username, t.FullText) } } -
Read the page. Each response holds up to 20 tweets and a
next_cursorfor the next page. The values below are demo data in the documented shape.Response200 OK { "tweets": [ { "id": "1985611093417201664", "full_text": "Rewrote our ingest worker with range-over-func iterators.", "created_at": "2026-11-01T21:14:52Z", "lang": "en", "likes_count": 57, "retweet_count": 9, "reply_count": 4, "quote_count": 2, "view_count": 4120, "bookmark_count": 11, "is_reply": false, "quoted_status": null, "user": { "id": "1601739518260654080", "username": "gopher_ingest_notes", "display_name": "Gopher Ingest Notes", "created_at": "2022-12-11T09:03:18Z", "followers_count": 2310 } } ], "next_cursor": "DAABCgABF7Zq0xLwAAA" }
Every tweet arrives with its counts and its author, so the request sets no expansions or field lists, and each request costs the same whatever it returns.
#Model the response with typed structs
Define one Tweet and one User struct for the fields your code reads, and every Sorsa API endpoint that returns tweets or users decodes into them, because the API uses the same two shapes everywhere. Put them in a small package, here sorsa, next to the client from the next section.
package sorsa
import "time"
type Tweet struct {
ID string `json:"id"`
FullText string `json:"full_text"`
CreatedAt time.Time `json:"created_at"`
Lang string `json:"lang"`
LikesCount int `json:"likes_count"`
RetweetCount int `json:"retweet_count"`
ReplyCount int `json:"reply_count"`
QuoteCount int `json:"quote_count"`
ViewCount int `json:"view_count"`
IsReply bool `json:"is_reply"`
User User `json:"user"`
QuotedStatus *Tweet `json:"quoted_status"`
RetweetedStatus *Tweet `json:"retweeted_status"`
}
type User struct {
ID string `json:"id"`
Username string `json:"username"`
DisplayName string `json:"display_name"`
Description string `json:"description"`
CreatedAt time.Time `json:"created_at"`
FollowersCount int `json:"followers_count"`
FollowingsCount int `json:"followings_count"`
TweetsCount int `json:"tweets_count"`
Verified bool `json:"verified"`
}
// TweetsPage and UsersPage are the two list shapes. NextCursor
// is empty on the last page.
type TweetsPage struct {
Tweets []Tweet `json:"tweets"`
NextCursor string `json:"next_cursor"`
}
type UsersPage struct {
Users []User `json:"users"`
NextCursor string `json:"next_cursor"`
}
Three choices in these structs matter more than the rest. IDs have 19 digits and arrive as JSON strings, so they decode into string: an int64 field stops the decoder with "cannot unmarshal string into Go struct field". Dates arrive in ISO 8601 UTC, which time.Time reads with no custom code. A quoted or reposted tweet is a tweet inside a tweet, so it needs a pointer: Go rejects a struct that contains itself by value, and the pointer stays nil when the post quotes nothing.
A tag that matches no JSON key is not an error in Go: the field just stays zero. Code moved from the official X API shows it first, because X names the count like_count inside public_metrics while Sorsa API returns likes_count on the tweet itself. Pick the fields you need below and copy a struct whose tags match the API reference.
import "time"
type Tweet struct {
ID string `json:"id"`
FullText string `json:"full_text"`
CreatedAt time.Time `json:"created_at"`
LikesCount int `json:"likes_count"`
ViewCount int `json:"view_count"`
User User `json:"user"`
}
type User struct {
ID string `json:"id"`
Username string `json:"username"`
FollowersCount int `json:"followers_count"`
}
The tags and types follow Sorsa API's live OpenAPI reference; unticked fields are simply skipped by encoding/json.
#Build one client with retries and a pacer
Put the key, the HTTP client, the retry rules and the rate pacer in one Client, and share that one value across every goroutine: http.Client is safe for concurrent use, and so is the ticker that paces the starts. Every later example calls its Do method.
package sorsa
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
)
const baseURL = "https://api.sorsa.io/v3"
// Client is safe for concurrent use: share one per API key.
type Client struct {
key string
http *http.Client
pace *time.Ticker
}
// New paces requests at 19 starts a second, one under the limit.
// Pass the key from os.Getenv("SORSA_API_KEY"), or "YOUR_API_KEY".
func New(key string) *Client {
return &Client{
key: key,
http: &http.Client{Timeout: 30 * time.Second},
pace: time.NewTicker(time.Second / 19),
}
}
// APIError is a reply other than 200, with the API's message.
type APIError struct {
Status int `json:"-"`
Message string `json:"message"`
}
func (e *APIError) Error() string {
return fmt.Sprintf("sorsa: %d %s", e.Status, e.Message)
}
// Do sends one request and decodes the reply into out. It retries
// 429 and 5xx up to 4 times, after 1, 2, 4 and 8 seconds.
func (c *Client) Do(ctx context.Context, method, path string,
query url.Values, body, out any) error {
var payload []byte
if body != nil {
var err error
if payload, err = json.Marshal(body); err != nil {
return err
}
}
u := baseURL + path
if len(query) > 0 {
u += "?" + query.Encode()
}
for attempt := 0; ; attempt++ {
select { // wait for a start slot
case <-c.pace.C:
case <-ctx.Done():
return ctx.Err()
}
req, err := http.NewRequestWithContext(ctx, method, u,
bytes.NewReader(payload))
if err != nil {
return err
}
req.Header.Set("ApiKey", c.key)
req.Header.Set("Content-Type", "application/json")
resp, err := c.http.Do(req)
if err != nil {
return err
}
if resp.StatusCode == http.StatusOK {
defer resp.Body.Close()
return json.NewDecoder(resp.Body).Decode(out)
}
apiErr := &APIError{Status: resp.StatusCode}
_ = json.NewDecoder(io.LimitReader(resp.Body, 4096)).Decode(apiErr)
resp.Body.Close()
retry := resp.StatusCode == http.StatusTooManyRequests ||
resp.StatusCode >= 500
if !retry || attempt == 4 {
return apiErr
}
select {
case <-time.After(time.Second << attempt):
case <-ctx.Done():
return ctx.Err()
}
}
}
A 429 means more than 20 requests started within one second on your key, and a 5xx is a temporary fault on the server side; both are worth a retry. A 401 or 403 is not: the key is wrong or missing, or its requests are used up, and Message says which. Failed requests are never charged, so a retry costs nothing, and every Sorsa API endpoint only reads, so retrying a POST such as /search-tweets can't create anything twice.
The ticker hands out one start every 1/19 of a second, and every attempt waits for one, retries included. At 19 a second the pacer leaves one start of headroom, so a tick saved while the program sat idle can never push a second past Sorsa API's limit of 20. The context ends both waits at once, so a cancelled job stops without sending the requests it had queued. Callers tell errors apart with errors.As:
var apiErr *sorsa.APIError
if errors.As(err, &apiErr) && apiErr.Status == http.StatusForbidden {
log.Fatal("check the key or its remaining requests: ", apiErr.Message)
}
#Page through results with an iterator
A cursor is an opaque key to the next page of results: send next_cursor back unchanged and stop when a response comes without one. Search and timelines return up to 20 tweets per page, and a page can hold fewer while more remain, so the item count never tells you that you reached the end. Since Go 1.23 a function can be the thing a for range loop walks, which keeps the whole cursor loop in one method:
package sorsa
import (
"context"
"iter"
"net/http"
"net/url"
)
// SearchPages yields pages of tweets until the cursor runs out
// or maxPages pages have been read.
func (c *Client) SearchPages(ctx context.Context, query string,
maxPages int) iter.Seq2[[]Tweet, error] {
return func(yield func([]Tweet, error) bool) {
body := map[string]string{"query": query, "order": "latest"}
for range maxPages {
var page TweetsPage
err := c.Do(ctx, http.MethodPost, "/search-tweets",
nil, body, &page)
if !yield(page.Tweets, err) || err != nil {
return
}
next := page.NextCursor
if next == "" || next == body["next_cursor"] {
return
}
body["next_cursor"] = next
}
}
}
// Followers yields an account's followers, up to 200 a page.
func (c *Client) Followers(ctx context.Context, username string,
maxPages int) iter.Seq2[[]User, error] {
return func(yield func([]User, error) bool) {
q := url.Values{"username": {username}}
for range maxPages {
var page UsersPage
err := c.Do(ctx, http.MethodGet, "/followers",
q, nil, &page)
if !yield(page.Users, err) || err != nil {
return
}
next := page.NextCursor
if next == "" || next == q.Get("next_cursor") {
return
}
q.Set("next_cursor", next)
}
}
}
The caller reads pages the way it reads any slice, and a break stops the requests on the spot:
client := sorsa.New(os.Getenv("SORSA_API_KEY"))
ctx := context.Background()
for tweets, err := range client.SearchPages(ctx, "golang lang:en", 50) {
if err != nil {
log.Fatal(err)
}
for _, t := range tweets {
fmt.Println(t.ID, t.CreatedAt.Format(time.DateOnly), t.User.Username)
}
}
POST endpoints take the cursor in the JSON body and GET endpoints take it as a query parameter, which is the only difference between the two methods. GET /followers (opens in a new tab) returns up to 200 profiles a page, so a list of one million followers takes about 5,000 requests; the guide to export Twitter followers writes such a list to CSV. Treat maxPages as a budget, so a broad query never spends more than you planned, and keep the query unchanged while you follow one cursor. The check against a repeated cursor is a cheap guard that ends a loop which would otherwise spend the whole budget on one page.
#Fetch 100 tweets or profiles per request
When you already hold IDs or usernames, send them in batches. POST /tweet-info-bulk (opens in a new tab) takes up to 100 tweet IDs or links, and GET /info-batch (opens in a new tab) takes up to 100 usernames, each in one request. slices.Chunk cuts the list for you:
var tweets []sorsa.Tweet
for batch := range slices.Chunk(tweetIDs, 100) {
var page sorsa.TweetsPage
body := map[string][]string{"tweet_links": batch}
err := client.Do(ctx, http.MethodPost, "/tweet-info-bulk",
nil, body, &page)
if err != nil {
return err
}
tweets = append(tweets, page.Tweets...)
}
var users []sorsa.User
for batch := range slices.Chunk(usernames, 100) {
var page sorsa.UsersPage
q := url.Values{"usernames": batch} // usernames=a&usernames=b
err := client.Do(ctx, http.MethodGet, "/info-batch", q, nil, &page)
if err != nil {
return err
}
users = append(users, page.Users...)
}
Every request costs the same whatever it returns, so a batch of 100 tweets costs what one tweet costs: from $0.014 per 1,000 tweets, more than 250x less than the official X API's $5.00. url.Values is a map of string slices, so a batch drops in as is and goes out as the repeated parameter GET /info-batch reads. Batch responses have no cursor: one response holds the whole batch, under tweets or under users.
#Run goroutines within 20 requests per second
Sorsa API allows 20 requests per second per key on every plan, and a Go program reaches that rate with a small pool of goroutines once the work splits into independent cursor chains. One chain can't be split, because each page needs the cursor from the page before. So give each goroutine whole chains, one account or one day of a search at a time, and let the shared client pace them all:
package sorsa
import (
"context"
"errors"
"fmt"
"sync"
)
// Collect runs every query through a pool of workers. They share
// one Client, so its pacer caps the key's total rate.
func Collect(ctx context.Context, c *Client, queries []string,
workers int) ([]Tweet, error) {
jobs := make(chan string)
var (
mu sync.Mutex
all []Tweet
errs []error
wg sync.WaitGroup
)
for range workers {
wg.Go(func() {
for q := range jobs {
for tweets, err := range c.SearchPages(ctx, q, 100) {
mu.Lock()
if err != nil {
errs = append(errs, fmt.Errorf("%s: %w", q, err))
} else {
all = append(all, tweets...)
}
mu.Unlock()
}
}
})
}
for _, q := range queries {
jobs <- q
}
close(jobs)
wg.Wait()
return all, errors.Join(errs...)
}
Split a long search into days with the since: and until: operators and hand the days to the pool:
start := time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC)
var days []string
for d := range 31 { // October 1 to November 1
day := start.AddDate(0, 0, d)
days = append(days, fmt.Sprintf("golang lang:en since:%s until:%s",
day.Format(time.DateOnly), day.AddDate(0, 0, 1).Format(time.DateOnly)))
}
tweets, err := sorsa.Collect(ctx, client, days, 20)
Twenty workers are plenty: most of their time goes to waiting for the next tick, and more workers only wait longer. wg.Go, added in Go 1.25, starts each goroutine and counts it in one call. Keep one Client per key and per process: two processes with a client each can start twice the limit between them, so give each process its own key or half the rate. At 20 requests a second, batches of 100 move up to 2,000 tweets a second, follower pages 4,000 profiles and search pages 400 tweets.
Good to know
Collectkeeps going when one day fails: it saves the error with the query that caused it and returns all of them througherrors.Joinat the end. To stop the whole run at the first failure, wrap the context withcontext.WithCanceland callcancel()where the error is saved; the pacer and the retry waits both return as soon as the context ends.
#What a Go job costs
Sorsa API charges per request, whatever the endpoint returns: tweets fetched in batches of 100 cost from $0.014 per 1,000, search results from $0.07, followers from $0.007, and failed requests are never charged. The official X API charges per object instead: $5.00 per 1,000 posts, and from November 22, 2026 up to $15.00 per 1,000 when the request expands each post's author.
Official X API
$5,000
for 1M tweets
$0.005 per post
Sorsa API
from$14
for 1M tweets
from $0.014 per 1,000
more than 250x cheaper
The rate per 1,000 requests runs from $4.90 on Starter to $1.40 on Hyperscale, and a plan applies it to every endpoint alike. A service can watch its balance for free: GET /key-usage-info (opens in a new tab) never counts against your requests.
var usage struct {
Remaining int `json:"remaining_requests"`
ValidUntil time.Time `json:"valid_until"`
}
err := client.Do(ctx, http.MethodGet, "/key-usage-info", nil, nil, &usage)
if err == nil {
fmt.Println(usage.Remaining, "left until", usage.ValidUntil)
}
#Moving gotwi or go-twitter code to Sorsa API
gotwi and go-twitter are thin layers over the official X API's v2 paths, so their calls map to Sorsa API by path. Each path below has one Sorsa API endpoint, and the code gets shorter: no expansions and no field lists, because every tweet already carries its author and counts.
| Official X API v2 path | Sorsa API endpoint | What changes |
|---|---|---|
GET /2/tweets/search/recent | POST /search-tweets | Any date back to 2006, not only the last 7 days |
GET /2/tweets/search/all | POST /search-tweets | The same endpoint with since: and until: |
GET /2/users/:id/tweets | POST /user-tweets (opens in a new tab) | A username is enough; no 3,200-post cap |
GET /2/tweets | POST /tweet-info-bulk | 100 IDs or links per request |
GET /2/users/by | GET /info-batch | 100 usernames per request |
GET /2/users/:id/followers | GET /followers | 200 followers per page |
GET /2/tweets/:id/retweeted_by | POST /retweeters (opens in a new tab) | The same cursor loop |
POST /2/tweets | none | Read-only: post through the official X API |
GET /2/tweets/search/recent- Sorsa API endpoint
POST /search-tweets- What changes
- Any date back to 2006, not only the last 7 days
GET /2/tweets/search/all- Sorsa API endpoint
POST /search-tweets- What changes
- The same endpoint with
since:anduntil:
GET /2/users/:id/tweets- Sorsa API endpoint
POST /user-tweets(opens in a new tab)- What changes
- A username is enough; no 3,200-post cap
GET /2/tweets- Sorsa API endpoint
POST /tweet-info-bulk- What changes
- 100 IDs or links per request
GET /2/users/by- Sorsa API endpoint
GET /info-batch- What changes
- 100 usernames per request
GET /2/users/:id/followers- Sorsa API endpoint
GET /followers- What changes
- 200 followers per page
GET /2/tweets/:id/retweeted_by- Sorsa API endpoint
POST /retweeters(opens in a new tab)- What changes
- The same cursor loop
POST /2/tweets- Sorsa API endpoint
- none
- What changes
- Read-only: post through the official X API
Field names change with the paths: text becomes full_text, public_metrics.like_count becomes likes_count, author_id with includes.users becomes the nested user, and meta.next_token becomes next_cursor. Rate-limit handling shrinks too, because X's 15-minute windows give way to one limit that resets every second. To move a whole codebase, every v2 endpoint and response field is mapped in the guide to migrating from the Twitter/X API.
#Run your first search from Go today
Getting X data into Go takes the standard library, one key and four pieces: typed structs, a Client with retries and a pacer, iterators over the cursor, and a worker pool for many chains. Build on Sorsa API, the Twitter API for reading public X data at scale: 20 tweets per search request from $0.07 per 1,000, batches of 100 from $0.014, 20 requests per second on every plan, and 100 free requests that need no card and never expire. The same calls in other languages are in the guides to the Twitter API in Node.js and the Twitter API in PHP.
#FAQ
Is there a free Twitter API for Go?
The official X API has no free tier for new developers: gotwi and the other Go clients are free, but every read spends prepaid credits at $0.005 per post. Sorsa API gives every new account 100 free requests that never expire and need no card. From Go they collect 2,000 tweets by search, or up to 10K tweets in batches of 100.
Which Twitter API should I use with Go?
For reading X data from Go, use Sorsa API: the standard library's net/http with one key instead of OAuth, search results from $0.07 per 1,000 tweets and batches from $0.014, 20 requests per second on every plan for goroutines to share, search back to 2006, and no X login, so your account is never at risk. To post as well, pair it with the official X API.
Can I get X data in Go without a developer account?
Yes. Sorsa API needs only its own key: sign up, set the ApiKey header and call any of its 45 endpoints from Go, with no X developer account, app review or OAuth. Go libraries that read X's website instead log in with an account's cookies or a pool of accounts, which puts those accounts at risk. The options are compared in Twitter API without a developer account.
Can I post tweets from Go with Sorsa API?
No. Sorsa API is read-only by design: it reads public posts, profiles and followers and never posts, likes, follows or sends messages, so a key can't act on any X account. To post from Go, call the official X API's POST /2/tweets with an OAuth user token, directly or through gotwi, at $0.015 a post, and keep Sorsa API for the reads.
Is there a Go SDK or module for Sorsa API?
No module is needed: Sorsa API is plain REST with JSON, and the client in this guide is about 100 lines of the standard library. The live OpenAPI reference at api.sorsa.io/v3/swagger.json lists all 45 endpoints with their fields, so a generator such as oapi-codegen can produce typed request and response code if you prefer generated types.
#How we checked
We wrote every request in this guide from Sorsa API's live OpenAPI reference (45 endpoints) and its documentation on rate limits and pagination, read on November 2, 2026, and every Go construct from the standard library's documentation and release notes (iterators and slices.Chunk from Go 1.23, WaitGroup.Go from Go 1.25; the current release is Go 1.27). We had no Sorsa API key and no Go toolchain on our machine for this pass, so the response shows demo values in the documented shape, and we checked each code block line by line against those documents instead of running it. The same day we read the official X API's price table, X's notice on expansion billing and its list of official SDKs and community libraries on docs.x.com, and the release history of gotwi, both go-twitter projects and xurl on GitHub.
Sources
- X API pay-per-usage pricing (docs.x.com) (opens in a new tab)
- X API PPU: expansions update (X Developers, October 23, 2026) (opens in a new tab)
- Tools and libraries: official SDKs and community libraries (docs.x.com) (opens in a new tab)
- gotwi releases (GitHub) (opens in a new tab)
- Go 1.23 release notes: range-over-func iterators (opens in a new tab)
- Go 1.25 release notes: WaitGroup.Go (opens in a new tab)
- Sorsa API reference, OpenAPI
- Sorsa API rate limits (opens in a new tab)
- Sorsa API pagination (opens in a new tab)