Official Go client for the Newsdata.io News API. Wraps
every endpoint (latest, archive, sources, crypto, market, count,
crypto/count, market/count) with client-side parameter validation,
automatic retries with exponential backoff, scroll/paginate helpers, and a
typed error hierarchy. Idiomatic Go: context.Context-aware, no external
runtime dependencies, safe for concurrent use.
go get github.com/newsdataapi/newsdata-go-client@latestGo modules pull the package straight from GitHub — no separate registry to configure.
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/newsdataapi/newsdata-go-client"
)
func main() {
client, err := newsdataapi.NewClient(os.Getenv("NEWSDATA_API_KEY"))
if err != nil {
log.Fatal(err)
}
resp, err := client.Latest(context.Background(), newsdataapi.Params{
"q": "bitcoin",
"country": []string{"us", "gb"},
"language": "en",
})
if err != nil {
log.Fatal(err)
}
articles, _ := resp.Articles()
for _, a := range articles {
fmt.Println(a.Title, "-", a.Link)
}
}| Method | Endpoint | Notes |
|---|---|---|
client.Latest(ctx, params) |
/1/latest |
Real-time news |
client.Archive(ctx, params) |
/1/archive |
Historical news |
client.Sources(ctx, params) |
/1/sources |
Available sources (single page) |
client.Crypto(ctx, params) |
/1/crypto |
Cryptocurrency news |
client.Market(ctx, params) |
/1/market |
Market / financial news |
client.Count(ctx, params) |
/1/count |
Aggregate counts (requires from_date, to_date) |
client.CryptoCount(ctx, params) |
/1/crypto/count |
Aggregate crypto counts |
client.MarketCount(ctx, params) |
/1/market/count |
Aggregate market counts |
Every value in Params may be a string, a []string (sent comma-joined),
a bool, an int, a float64, or *bool / *float64 if you need to
distinguish "unset" from a zero value. Parameter names are case-insensitive —
qInTitle and qintitle are equivalent.
Two helpers; both take an endpoint constant (EndpointLatest, EndpointArchive, …):
// 1) ScrollAll: follow nextPage cursors, return one merged Response.
all, err := client.ScrollAll(ctx, newsdataapi.EndpointLatest,
newsdataapi.Params{"q": "news"}, 200)
articles, _ := all.Articles() // all 200 in one slice
// 2) Paginate: callback per page. Return false from yield to stop early.
err = client.Paginate(ctx, newsdataapi.EndpointLatest,
newsdataapi.Params{"q": "news"},
func(page *newsdataapi.Response, err error) bool {
if err != nil {
return false
}
arts, _ := page.Articles()
process(arts)
return true
})resp, err := client.Latest(ctx, newsdataapi.Params{
"rawQuery": "q=bitcoin&country=us&language=en",
})rawQuery is mutually exclusive with all other parameters and is validated
against the endpoint's allowed keys before the request is sent.
Before any request leaves the process, parameters are validated and a typed
*NewsdataValidationError is returned (no API quota spent) when:
- a parameter is not accepted by that endpoint;
- mutually-exclusive parameters are set together —
q/qInTitle/qInMeta,country/excludecountry,category/excludecategory,language/excludelanguage,domain/domainurl/excludedomain; sizeis outside 1–50;sentiment_scoreis set withoutsentiment;- a count endpoint is missing
from_dateorto_date.
Booleans for full_content, image, video, and removeduplicate are
coerced to "1"/"0".
All SDK errors satisfy the typed hierarchy and play nicely with errors.As
and errors.Is:
import "errors"
var (
ve *newsdataapi.NewsdataValidationError
ae *newsdataapi.NewsdataAuthError
rl *newsdataapi.NewsdataRateLimitError
se *newsdataapi.NewsdataServerError
ne *newsdataapi.NewsdataNetworkError
api *newsdataapi.NewsdataAPIError
)
switch {
case errors.As(err, &ve): /* invalid parameter: ve.Param, ve.Message */
case errors.As(err, &ae): /* 401 / 403: ae.StatusCode */
case errors.As(err, &rl): /* 429: rl.RetryAfter (seconds) */
case errors.As(err, &se): /* 5xx */
case errors.As(err, &ne): /* network/timeout/cancellation: ne.Err */
case errors.As(err, &api): /* other API errors: api.StatusCode, api.Message */
}Hierarchy:
NewsdataError (catch-all base)
NewsdataValidationError (.Param, .Message)
NewsdataAPIError (.StatusCode, .Message, .ResponseBody)
├── NewsdataAuthError (401 / 403)
├── NewsdataRateLimitError (429; .RetryAfter)
└── NewsdataServerError (5xx)
NewsdataNetworkError (.Err)
client, err := newsdataapi.NewClient(apiKey,
newsdataapi.WithTimeout(30 * time.Second), // per-request
newsdataapi.WithMaxRetries(5), // 1 = no retry
newsdataapi.WithRetryBackoff(2 * time.Second), // base, exponential
newsdataapi.WithRetryBackoffMax(60 * time.Second), // cap on a single sleep
newsdataapi.WithPaginationDelay(time.Second),
newsdataapi.WithBaseURL("https://staging.example/api/1/"),
newsdataapi.WithHTTPClient(myCustomClient), // proxies / mTLS
newsdataapi.WithIncludeHeaders(true), // attach Response.ResponseHeaders
newsdataapi.WithLogger(myLogger), // any Info(msg)/Warn(msg) value
)Retries cover network errors, HTTP 429, and 5xx responses. 429 honours the
Retry-After header (integer seconds or HTTP-date); otherwise the wait is
exponential. Auth and other 4xx errors are never retried. context.Context
cancellation interrupts retries immediately — passing a cancelled or
deadlined context returns a NewsdataNetworkError wrapping context.Canceled
or context.DeadlineExceeded.
Client is safe for concurrent use by multiple goroutines.
go test -race ./... # full unit suite (no API key required)
go vet ./...
go build ./examples/...The test suite uses net/http/httptest to mock the API end-to-end — no
network access required. 30 tests cover the validator, request loop, retry
behaviour, scroll + paginate, error mapping, and API-key redaction.