API do Twitter em Go (Golang): código que funciona
Para obter dados do Twitter/X em Go, envie requisições HTTPS com net/http, decodifique o JSON em structs tipadas e siga o cursor página por página. O X não oferece SDK oficial em Go, e a API oficial do X cobra US$ 0,005 por post lido.
Resposta curta: O X publica SDKs oficiais apenas para Python e TypeScript, então o código em Go chama a API oficial do X por net/http ou por uma biblioteca da comunidade como a gotwi, com uma conta de desenvolvedor e créditos pré-pagos: US$ 0,005 por post. Para dados públicos, use a Sorsa API com net/http: uma chave, 20 tweets por requisição de busca a partir de US$ 0,07 por 1.000; 100 requisições grátis cobrem 2.000.
- Endpoint
POST /search-tweets- Tamanho da página
- 20 tweets por requisição
- Preço
- a partir de US$ 0,07 por 1.000 tweets
#O X publica um SDK oficial em Go?
Não. A documentação para desenvolvedores do X lista SDKs oficiais apenas para Python e TypeScript e coloca o Go entre as bibliotecas da comunidade, que o X não mantém (página de ferramentas e bibliotecas do X (abre em uma nova aba)). O único projeto oficial do X em Go é o xurl, uma ferramenta de linha de comando que você executa em um terminal, não um pacote que você importa.
Sobram os clientes da comunidade, e só um deles ainda lança releases. As datas abaixo vêm do histórico de cada projeto no GitHub:
| Cliente Go | Último release | Situação em 2 de novembro de 2026 |
|---|---|---|
| gotwi | v0.18.5, 21 de agosto de 2026 | Mantido; cobre a maioria dos endpoints da v2 com OAuth 1.0a, bearer token ou PKCE |
| go-twitter v2 (g8rswimmer) | v2.1.5, 29 de dezembro de 2022 | Nenhum release em quase quatro anos |
| go-twitter (dghubble) | arquivado em 2022 | Escrito para a API v1.1 aposentada |
gotwi
- Último release
- v0.18.5, 21 de agosto de 2026
- Situação em 2 de novembro de 2026
- Mantido; cobre a maioria dos endpoints da v2 com OAuth 1.0a, bearer token ou PKCE
go-twitter v2 (g8rswimmer)
- Último release
- v2.1.5, 29 de dezembro de 2022
- Situação em 2 de novembro de 2026
- Nenhum release em quase quatro anos
go-twitter (dghubble)
- Último release
- arquivado em 2022
- Situação em 2 de novembro de 2026
- Escrito para a API v1.1 aposentada
Uma biblioteca só molda as requisições. Cada chamada ainda gasta os créditos pré-pagos do X: US$ 0,005 por post e US$ 0,010 por usuário, até 3 milhões de leituras de posts por mês (tabela de preços do X (abre em uma nova aba)). A partir de 22 de novembro de 2026, os autores e as mídias que voltam por expansions são cobrados às mesmas tarifas (aviso do X de 23 de outubro (abre em uma nova aba)), então a busca que a maior parte do código em Go envia custa até três vezes o preço do post:
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
O que a rota oficial exige antes da primeira chamada
- Uma conta de desenvolvedor em console.x.com, aberta com uma conta do X, com o Developer Agreement do X aceito.
- Um app e o seu bearer token, que o X mostra uma única vez.
- Créditos comprados com antecedência e gastos a cada post e usuário que uma resposta devolve.
- Tokens de usuário OAuth para tudo que poste, curta ou siga.
Ler posts, perfis e seguidores públicos não exige nada disso. A mesma escolha para Tweepy e xdk está no guia API do Twitter em Python; o resto desta página faz o trabalho em Go.
#Obtenha tweets em Go com uma chave de API
Para ler dados do X em Go, use a Sorsa API, uma API e scraper do Twitter (X): um header ApiKey no lugar do OAuth, 20 requisições por segundo em todos os planos, contas gratuitas incluídas, busca desde 2006 e resultados de busca a partir de US$ 0,07 por 1.000 tweets com o perfil completo do autor dentro de cada um. A biblioteca padrão é o cliente inteiro: o net/http envia a requisição e o encoding/json lê a resposta, então o go.mod não ganha nenhuma dependência. A Sorsa API nunca pede o seu login do X, então sua conta do X não corre risco.
-
Obtenha uma chave. Cadastre-se e copie a chave de API do painel. As primeiras 100 requisições são grátis, não exigem cartão e nunca expiram.
-
Inicie um módulo e guarde a chave. Mantenha a chave em uma variável de ambiente e chame a API só de código no servidor, nunca de um navegador ou de um app entregue a usuários. O código deste guia exige Go 1.25 ou mais recente.
Terminal go mod init example.com/xdata export SORSA_API_KEY="your_key" # PowerShell: $env:SORSA_API_KEY="your_key" -
Busque tweets. Envie uma consulta na sintaxe de busca do X ao
POST /search-tweets(abre em uma nova aba) com a chave no headerApiKeye depois rodego 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) } } -
Leia a página. Cada resposta traz até 20 tweets e um
next_cursorpara a página seguinte. Os valores abaixo são dados de demonstração no formato documentado.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" }
Todo tweet chega com suas contagens e seu autor, então a requisição não define expansions nem listas de campos, e cada requisição custa o mesmo, seja qual for o retorno.
#Modele a resposta com structs tipadas
Defina uma struct Tweet e uma struct User com os campos que o seu código lê, e todo endpoint da Sorsa API que devolve tweets ou usuários decodifica nelas, porque a API usa os mesmos dois formatos em todo lugar. Coloque-as em um pacote pequeno, aqui sorsa, ao lado do cliente da próxima seção.
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"`
}
Três escolhas nessas structs importam mais que as demais. Os IDs têm 19 dígitos e chegam como strings JSON, então decodificam em string: um campo int64 interrompe o decodificador com "cannot unmarshal string into Go struct field". As datas chegam em ISO 8601 UTC, que o time.Time lê sem nenhum código extra. Um tweet citado ou repostado é um tweet dentro de um tweet, então precisa de um ponteiro: o Go rejeita uma struct que contém a si mesma por valor, e o ponteiro continua nil quando o post não cita nada.
Uma tag que não corresponde a nenhuma chave do JSON não é um erro em Go: o campo simplesmente fica zerado. O código vindo da API oficial do X mostra isso primeiro, porque o X chama a contagem de like_count dentro de public_metrics, enquanto a Sorsa API devolve likes_count no próprio tweet. Escolha abaixo os campos de que precisa e copie uma struct cujas tags correspondam à referência da API.
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"`
}
As tags e os tipos seguem a referência OpenAPI ao vivo da Sorsa API; campos desmarcados são simplesmente ignorados pelo encoding/json.
#Monte um cliente com retentativas e um pacer
Coloque a chave, o cliente HTTP, as regras de retentativa e o pacer de taxa em um único Client e compartilhe esse valor entre todas as goroutines: o http.Client é seguro para uso concorrente, e o ticker que dita o ritmo das partidas também. Todo exemplo seguinte chama o seu método Do.
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()
}
}
}
Um 429 significa que mais de 20 requisições começaram em um segundo na sua chave, e um 5xx é uma falha temporária do lado do servidor; os dois merecem uma nova tentativa. Um 401 ou 403 não: a chave está errada ou ausente, ou suas requisições acabaram, e a Message diz qual dos casos. Requisições com falha nunca são cobradas, então uma nova tentativa não custa nada, e todo endpoint da Sorsa API só lê, então repetir um POST como o /search-tweets não consegue criar nada em duplicidade.
O ticker libera uma partida a cada 1/19 de segundo, e toda tentativa espera por uma, as retentativas inclusive. A 19 por segundo o pacer deixa uma partida de folga, de modo que um tick guardado enquanto o programa estava parado nunca consegue empurrar um segundo além do limite de 20 da Sorsa API. O contexto encerra as duas esperas de uma vez, então um trabalho cancelado para sem enviar as requisições que tinha na fila. Quem chama distingue os erros com 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)
}
#Pagine os resultados com um iterador
Um cursor é uma chave opaca para a próxima página de resultados: devolva o next_cursor sem alterá-lo e pare quando uma resposta vier sem ele. A busca e as timelines devolvem até 20 tweets por página, e uma página pode ter menos enquanto ainda restam outras, então a contagem de itens nunca diz que você chegou ao fim. Desde o Go 1.23, uma função pode ser aquilo que um laço for range percorre, o que mantém todo o laço do cursor em um único método:
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)
}
}
}
Quem chama lê as páginas como lê qualquer slice, e um break interrompe as requisições na hora:
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)
}
}
Os endpoints POST recebem o cursor no corpo JSON e os endpoints GET o recebem como parâmetro de consulta, que é a única diferença entre os dois métodos. O GET /followers (abre em uma nova aba) devolve até 200 perfis por página, então uma lista de um milhão de seguidores leva cerca de 5.000 requisições; o guia para exportar seguidores do Twitter grava uma lista dessas em CSV. Trate o maxPages como um orçamento, para que uma consulta ampla nunca gaste mais do que você planejou, e mantenha a consulta inalterada enquanto segue um cursor. A verificação contra um cursor repetido é uma proteção barata que encerra um laço que, de outra forma, gastaria todo o orçamento em uma única página.
#Busque 100 tweets ou perfis por requisição
Quando você já tem IDs ou usernames, envie-os em lotes. O POST /tweet-info-bulk (abre em uma nova aba) recebe até 100 IDs ou links de tweets, e o GET /info-batch (abre em uma nova aba) recebe até 100 usernames, cada um em uma requisição. O slices.Chunk corta a lista para você:
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...)
}
Cada requisição custa o mesmo, seja qual for o retorno, então um lote de 100 tweets custa o que custa um tweet: a partir de US$ 0,014 por 1.000 tweets, mais de 250x menos que os US$ 5,00 da API oficial do X. O url.Values é um mapa de slices de strings, então um lote entra como está e sai como o parâmetro repetido que o GET /info-batch lê. As respostas em lote não têm cursor: uma resposta traz o lote inteiro, em tweets ou em users.
#Rode goroutines dentro de 20 requisições por segundo
A Sorsa API permite 20 requisições por segundo por chave em todos os planos, e um programa em Go atinge essa taxa com um pequeno pool de goroutines quando o trabalho se divide em cadeias de cursor independentes. Uma cadeia não pode ser dividida, porque cada página precisa do cursor da página anterior. Então dê a cada goroutine cadeias inteiras, uma conta ou um dia de uma busca por vez, e deixe o cliente compartilhado ditar o ritmo de todas:
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...)
}
Divida uma busca longa em dias com os operadores since: e until: e entregue os dias ao 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)
Vinte workers bastam: a maior parte do tempo deles vai para esperar o próximo tick, e mais workers só esperam por mais tempo. O wg.Go, adicionado no Go 1.25, inicia cada goroutine e a contabiliza em uma só chamada. Mantenha um Client por chave e por processo: dois processos com um cliente cada podem iniciar o dobro do limite entre eles, então dê a cada processo a sua própria chave ou metade da taxa. A 20 requisições por segundo, lotes de 100 movem até 2.000 tweets por segundo, páginas de seguidores 4.000 perfis e páginas de busca 400 tweets.
Bom saber
O
Collectcontinua quando um dia falha: ele guarda o erro junto com a consulta que o causou e devolve todos eles porerrors.Joinno final. Para interromper a execução inteira na primeira falha, envolva o contexto comcontext.WithCancele chamecancel()onde o erro é guardado; o pacer e as esperas de retentativa retornam assim que o contexto termina.
#Quanto custa um job em Go
A Sorsa API cobra por requisição, seja qual for o retorno do endpoint: tweets buscados em lotes de 100 custam a partir de US$ 0,014 por 1.000, resultados de busca a partir de US$ 0,07, seguidores a partir de US$ 0,007, e requisições com falha nunca são cobradas. A API oficial do X cobra por objeto: US$ 5,00 por 1.000 posts e, a partir de 22 de novembro de 2026, até US$ 15,00 por 1.000 quando a requisição expande o autor de cada post.
API oficial do X
US$ 5.000
para 1 milhão tweets
US$ 0,005 por post
Sorsa API
a partir deUS$ 14
para 1 milhão tweets
a partir de US$ 0,014 por 1.000
mais de 250x mais barato
A tarifa por 1.000 requisições vai de US$ 4,90 no Starter a US$ 1,40 no Hyperscale, e um plano a aplica igualmente a todos os endpoints. Um serviço pode acompanhar o saldo de graça: o GET /key-usage-info (abre em uma nova aba) nunca desconta das suas requisições.
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)
}
#Migrando código do gotwi ou do go-twitter para a Sorsa API
O gotwi e o go-twitter são camadas finas sobre os caminhos da v2 da API oficial do X, então suas chamadas se mapeiam para a Sorsa API por caminho. Cada caminho abaixo tem um endpoint da Sorsa API, e o código fica mais curto: sem expansions e sem listas de campos, porque todo tweet já traz o seu autor e as contagens.
| Caminho da v2 da API oficial do X | Endpoint da Sorsa API | O que muda |
|---|---|---|
GET /2/tweets/search/recent | POST /search-tweets | Qualquer data desde 2006, não só os últimos 7 dias |
GET /2/tweets/search/all | POST /search-tweets | O mesmo endpoint com since: e until: |
GET /2/users/:id/tweets | POST /user-tweets (abre em uma nova aba) | Basta um username; sem o teto de 3.200 posts |
GET /2/tweets | POST /tweet-info-bulk | 100 IDs ou links por requisição |
GET /2/users/by | GET /info-batch | 100 usernames por requisição |
GET /2/users/:id/followers | GET /followers | 200 seguidores por página |
GET /2/tweets/:id/retweeted_by | POST /retweeters (abre em uma nova aba) | O mesmo laço de cursor |
POST /2/tweets | nenhum | Somente leitura: poste pela API oficial do X |
GET /2/tweets/search/recent- Endpoint da Sorsa API
POST /search-tweets- O que muda
- Qualquer data desde 2006, não só os últimos 7 dias
GET /2/tweets/search/all- Endpoint da Sorsa API
POST /search-tweets- O que muda
- O mesmo endpoint com
since:euntil:
GET /2/users/:id/tweets- Endpoint da Sorsa API
POST /user-tweets(abre em uma nova aba)- O que muda
- Basta um username; sem o teto de 3.200 posts
GET /2/tweets- Endpoint da Sorsa API
POST /tweet-info-bulk- O que muda
- 100 IDs ou links por requisição
GET /2/users/by- Endpoint da Sorsa API
GET /info-batch- O que muda
- 100 usernames por requisição
GET /2/users/:id/followers- Endpoint da Sorsa API
GET /followers- O que muda
- 200 seguidores por página
GET /2/tweets/:id/retweeted_by- Endpoint da Sorsa API
POST /retweeters(abre em uma nova aba)- O que muda
- O mesmo laço de cursor
POST /2/tweets- Endpoint da Sorsa API
- nenhum
- O que muda
- Somente leitura: poste pela API oficial do X
Os nomes dos campos mudam com os caminhos: text vira full_text, public_metrics.like_count vira likes_count, author_id com includes.users vira o user aninhado e meta.next_token vira next_cursor. O tratamento de rate limit também encolhe, porque as janelas de 15 minutos do X dão lugar a um único limite que se reinicia a cada segundo. Para migrar uma base de código inteira, cada endpoint e campo de resposta da v2 está mapeado no guia de migração da API do Twitter/X.
#Rode hoje a sua primeira busca em Go
Levar dados do X para o Go exige a biblioteca padrão, uma chave e quatro peças: structs tipadas, um Client com retentativas e um pacer, iteradores sobre o cursor e um worker pool para muitas cadeias. Construa sobre a Sorsa API, a API do Twitter para ler dados públicos do X em escala: 20 tweets por requisição de busca a partir de US$ 0,07 por 1.000, lotes de 100 a partir de US$ 0,014, 20 requisições por segundo em todos os planos e 100 requisições grátis que não exigem cartão e nunca expiram. As mesmas chamadas em outras linguagens estão nos guias da API do Twitter em Node.js e da API do Twitter em PHP.
#Perguntas frequentes
Existe uma API do Twitter gratuita para Go?
A API oficial do X não tem plano gratuito para novos desenvolvedores: o gotwi e os outros clientes Go são gratuitos, mas cada leitura gasta créditos pré-pagos a US$ 0,005 por post. A Sorsa API dá a toda conta nova 100 requisições grátis que nunca expiram e não exigem cartão. Em Go, elas coletam 2.000 tweets por busca ou até 10 mil tweets em lotes de 100.
Qual API do Twitter devo usar com Go?
Para ler dados do X em Go, use a Sorsa API: o net/http da biblioteca padrão com uma chave no lugar do OAuth, resultados de busca a partir de US$ 0,07 por 1.000 tweets e lotes a partir de US$ 0,014, 20 requisições por segundo em todos os planos para as goroutines dividirem, busca desde 2006 e nenhum login do X, então sua conta nunca corre risco. Para também postar, combine-a com a API oficial do X.
Posso obter dados do X em Go sem uma conta de desenvolvedor?
Sim. A Sorsa API precisa apenas da própria chave: cadastre-se, defina o header ApiKey e chame qualquer um dos seus 45 endpoints em Go, sem conta de desenvolvedor do X, revisão de app nem OAuth. As bibliotecas Go que leem o site do X entram com cookies de uma conta ou com um conjunto de contas, o que coloca essas contas em risco. As opções são comparadas em API do Twitter sem conta de desenvolvedor.
Posso postar tweets em Go com a Sorsa API?
Não. A Sorsa API é somente leitura por princípio: ela lê posts, perfis e seguidores públicos e nunca posta, curte, segue nem envia mensagens, então uma chave não consegue agir em nenhuma conta do X. Para postar em Go, chame o POST /2/tweets da API oficial do X com um token de usuário OAuth, direto ou pelo gotwi, a US$ 0,015 por post, e mantenha a Sorsa API para as leituras.
Existe um SDK ou módulo Go para a Sorsa API?
Nenhum módulo é necessário: a Sorsa API é REST puro com JSON, e o cliente deste guia tem cerca de 100 linhas da biblioteca padrão. A referência OpenAPI em produção em api.sorsa.io/v3/swagger.json lista todos os 45 endpoints com seus campos, então um gerador como o oapi-codegen pode produzir código tipado de requisição e resposta se você preferir tipos gerados.
#Como verificamos
Escrevemos cada requisição deste guia a partir da referência OpenAPI em produção da Sorsa API (45 endpoints) e da sua documentação sobre rate limits e paginação, lidas em 2 de novembro de 2026, e cada construção em Go a partir da documentação e das notas de release da biblioteca padrão (iteradores e slices.Chunk do Go 1.23, WaitGroup.Go do Go 1.25; o release atual é o Go 1.27). Não tínhamos uma chave da Sorsa API nem o toolchain do Go na nossa máquina para esta rodada, então a resposta mostra valores de demonstração no formato documentado, e conferimos cada bloco de código linha por linha com esses documentos em vez de executá-lo. No mesmo dia, lemos a tabela de preços da API oficial do X, o aviso do X sobre a cobrança de expansions e sua lista de SDKs oficiais e bibliotecas da comunidade em docs.x.com, e o histórico de releases do gotwi, dos dois projetos go-twitter e do xurl no GitHub.
Fontes
- X API pay-per-usage pricing (docs.x.com) (abre em uma nova aba)
- X API PPU: expansions update (X Developers, October 23, 2026) (abre em uma nova aba)
- Tools and libraries: official SDKs and community libraries (docs.x.com) (abre em uma nova aba)
- gotwi releases (GitHub) (abre em uma nova aba)
- Go 1.23 release notes: range-over-func iterators (abre em uma nova aba)
- Go 1.25 release notes: WaitGroup.Go (abre em uma nova aba)
- Sorsa API reference, OpenAPI
- Sorsa API rate limits (abre em uma nova aba)
- Sorsa API pagination (abre em uma nova aba)