newsdataapi

package module
Version: v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 28, 2026 License: MIT Imports: 12 Imported by: 0

README

Newsdata.io Go Client

Go Reference CI Go License

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.

Installation

go get github.com/newsdataapi/newsdata-go-client@latest

Go modules pull the package straight from GitHub — no separate registry to configure.

Quickstart

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)
    }
}

Endpoints

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.

Pagination

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
    })

Raw query

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.

Client-side validation

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;
  • size is outside 1–50;
  • sentiment_score is set without sentiment;
  • a count endpoint is missing from_date or to_date.

Booleans for full_content, image, video, and removeduplicate are coerced to "1"/"0".

Error handling

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)

Configuration

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.

Concurrency

Client is safe for concurrent use by multiple goroutines.

Development

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.

License

MIT

Documentation

Overview

Package newsdataapi is the official Go client for the Newsdata.io REST API.

It wraps every endpoint (latest, archive, sources, crypto, market, count, crypto/count, market/count) with client-side parameter validation, retries with exponential backoff, scroll/paginate helpers, and a typed error hierarchy.

Quickstart:

client, err := newsdataapi.NewClient("YOUR_API_KEY")
if err != nil {
    log.Fatal(err)
}
resp, err := client.Latest(context.Background(), newsdataapi.LatestParams{
    Q:        "bitcoin",
    Country:  []string{"us", "gb"},
    Language: []string{"en"},
})
if err != nil {
    log.Fatal(err)
}
articles, _ := resp.Articles()
for _, a := range articles {
    fmt.Println(a.Title, "-", a.Link)
}

All methods accept a context.Context for cancellation and deadlines. Errors are typed (NewsdataValidationError, NewsdataAuthError, NewsdataRateLimitError, NewsdataServerError, NewsdataAPIError, NewsdataNetworkError) and can be inspected with errors.As.

Index

Constants

View Source
const (
	EndpointLatest      = "latest"
	EndpointArchive     = "archive"
	EndpointCrypto      = "crypto"
	EndpointSources     = "sources"
	EndpointMarket      = "market"
	EndpointCount       = "count"
	EndpointCryptoCount = "crypto_count"
	EndpointMarketCount = "market_count"
)

Endpoint name constants. These are also the strings ScrollAll and Paginate accept for their `endpoint` argument.

Variables

View Source
var (
	ErrValidation = errors.New("newsdataapi: validation error")
	ErrAPI        = errors.New("newsdataapi: API error")
	ErrNetwork    = errors.New("newsdataapi: network error")
)

Sentinel for callers that want a single error.Is check.

Functions

This section is empty.

Types

type Article

type Article struct {
	ArticleID      string         `json:"article_id"`
	Title          string         `json:"title"`
	Link           string         `json:"link"`
	Description    string         `json:"description,omitempty"`
	Content        string         `json:"content,omitempty"`
	Keywords       []string       `json:"keywords,omitempty"`
	Creator        []string       `json:"creator,omitempty"`
	VideoURL       string         `json:"video_url,omitempty"`
	ImageURL       string         `json:"image_url,omitempty"`
	PubDate        string         `json:"pubDate,omitempty"`
	PubDateTZ      string         `json:"pubDateTZ,omitempty"`
	SourceID       string         `json:"source_id,omitempty"`
	SourcePriority int            `json:"source_priority,omitempty"`
	SourceURL      string         `json:"source_url,omitempty"`
	SourceIcon     string         `json:"source_icon,omitempty"`
	SourceName     string         `json:"source_name,omitempty"`
	Language       string         `json:"language,omitempty"`
	Country        []string       `json:"country,omitempty"`
	Category       []string       `json:"category,omitempty"`
	AITag          []string       `json:"ai_tag,omitempty"`
	AIRegion       []string       `json:"ai_region,omitempty"`
	AIOrg          []string       `json:"ai_org,omitempty"`
	Sentiment      string         `json:"sentiment,omitempty"`
	SentimentStats map[string]any `json:"sentiment_stats,omitempty"`
	DataType       string         `json:"datatype,omitempty"`
}

Article is the typed shape of a single news result from the API. Fields use snake_case JSON tags to match the API; AI-enriched fields (ai_*) are set on articles where the platform produced them and are otherwise empty.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the HTTP client for the Newsdata.io API. Construct with NewClient and call the per-endpoint methods (Latest, Archive, ...) — those return a *Response and any client-side validation error or API-mapped error.

Client is safe for concurrent use by multiple goroutines.

func NewClient

func NewClient(apiKey string, opts ...Option) (*Client, error)

NewClient constructs a Client. apiKey is required; opts override the defaults listed in each WithXxx helper.

func (*Client) Archive

func (c *Client) Archive(ctx context.Context, params Params) (*Response, error)

Archive fetches historical news. GET /1/archive.

func (*Client) Count

func (c *Client) Count(ctx context.Context, params Params) (*Response, error)

Count fetches aggregate news counts for a date range. GET /1/count. Requires "from_date" and "to_date" in params.

func (*Client) Crypto

func (c *Client) Crypto(ctx context.Context, params Params) (*Response, error)

Crypto fetches cryptocurrency news. GET /1/crypto.

func (*Client) CryptoCount

func (c *Client) CryptoCount(ctx context.Context, params Params) (*Response, error)

CryptoCount fetches aggregate crypto counts. GET /1/crypto/count. Requires "from_date" and "to_date" in params.

func (*Client) Latest

func (c *Client) Latest(ctx context.Context, params Params) (*Response, error)

Latest fetches real-time news. GET /1/latest.

func (*Client) Market

func (c *Client) Market(ctx context.Context, params Params) (*Response, error)

Market fetches market / financial news. GET /1/market.

func (*Client) MarketCount

func (c *Client) MarketCount(ctx context.Context, params Params) (*Response, error)

MarketCount fetches aggregate market counts. GET /1/market/count. Requires "from_date" and "to_date" in params.

func (*Client) Paginate

func (c *Client) Paginate(
	ctx context.Context,
	endpoint string,
	params Params,
	yield func(*Response, error) bool,
) error

Paginate calls the endpoint once per page, invoking yield on each. Return false from yield to stop early; the function returns the error yield was called with (if any) or nil on clean completion.

endpoint must be one of the Endpoint* constants. Sources is not supported.

func (*Client) ScrollAll

func (c *Client) ScrollAll(ctx context.Context, endpoint string, params Params, maxResults int) (*Response, error)

ScrollAll follows nextPage cursors and returns one merged Response, capped at maxResults articles (0 = no cap, follow until exhaustion).

For news endpoints, Results is the concatenation of every page's results. For count endpoints (where the final page returns an aggregate object), the aggregate is placed in the returned Response as Results and the article arrays are concatenated separately (see Articles() / Aggregate()).

endpoint must be one of the Endpoint* constants. Sources is not supported.

func (*Client) Sources

func (c *Client) Sources(ctx context.Context, params Params) (*Response, error)

Sources lists available news sources. GET /1/sources. Single-page endpoint; ScrollAll and Paginate are not supported for this one.

type Logger

type Logger interface {
	Info(msg string)
	Warn(msg string)
}

Logger is the minimal interface the client uses for diagnostic logs. It is compatible with anything that has Info / Warn methods (slog.Logger does, and any custom logger can be adapted with a thin wrapper).

type NewsdataAPIError

type NewsdataAPIError struct {
	StatusCode   int
	Message      string
	ResponseBody []byte
}

NewsdataAPIError indicates the API returned a structured error response.

func (*NewsdataAPIError) Error

func (e *NewsdataAPIError) Error() string

func (*NewsdataAPIError) Is

func (e *NewsdataAPIError) Is(target error) bool

type NewsdataAuthError

type NewsdataAuthError struct{ *NewsdataAPIError }

NewsdataAuthError is raised on 401 / 403 responses.

func (*NewsdataAuthError) Error

func (e *NewsdataAuthError) Error() string

func (*NewsdataAuthError) Unwrap

func (e *NewsdataAuthError) Unwrap() error

Unwrap allows `errors.As` to also match NewsdataAPIError on this type.

type NewsdataError

type NewsdataError struct {
	Op  string // optional: the API operation, e.g. "latest"
	Msg string
	Err error
}

NewsdataError is the base type wrapping every SDK-originated error.

func (*NewsdataError) Error

func (e *NewsdataError) Error() string

func (*NewsdataError) Unwrap

func (e *NewsdataError) Unwrap() error

type NewsdataNetworkError

type NewsdataNetworkError struct {
	Err error
}

NewsdataNetworkError indicates a transport-level failure (DNS, TLS, timeout, context cancellation) prevented the request from completing.

func (*NewsdataNetworkError) Error

func (e *NewsdataNetworkError) Error() string

func (*NewsdataNetworkError) Is

func (e *NewsdataNetworkError) Is(target error) bool

func (*NewsdataNetworkError) Unwrap

func (e *NewsdataNetworkError) Unwrap() error

type NewsdataRateLimitError

type NewsdataRateLimitError struct {
	*NewsdataAPIError
	RetryAfter int // seconds, when the Retry-After header was parseable; 0 otherwise
}

NewsdataRateLimitError is raised on 429 responses once retries are exhausted.

func (*NewsdataRateLimitError) Error

func (e *NewsdataRateLimitError) Error() string

func (*NewsdataRateLimitError) Unwrap

func (e *NewsdataRateLimitError) Unwrap() error

type NewsdataServerError

type NewsdataServerError struct{ *NewsdataAPIError }

NewsdataServerError is raised on 5xx responses once retries are exhausted.

func (*NewsdataServerError) Error

func (e *NewsdataServerError) Error() string

func (*NewsdataServerError) Unwrap

func (e *NewsdataServerError) Unwrap() error

type NewsdataValidationError

type NewsdataValidationError struct {
	Param   string // The offending parameter name, when known.
	Message string
}

NewsdataValidationError indicates a parameter failed client-side validation. No request was sent.

func (*NewsdataValidationError) Error

func (e *NewsdataValidationError) Error() string

func (*NewsdataValidationError) Is

func (e *NewsdataValidationError) Is(target error) bool

Is hooks so callers can use errors.Is(err, ErrValidation) etc.

type Option

type Option func(*Client) error

Option configures a Client at construction time.

func WithBaseURL

func WithBaseURL(u string) Option

WithBaseURL overrides the API base URL (useful for staging / a proxy). The trailing slash is added if missing.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient injects a custom *http.Client (e.g. with a custom Transport for proxies, mTLS, instrumentation). Note: setting this overrides the WithTimeout value — set the timeout on your own client instead.

func WithIncludeHeaders

func WithIncludeHeaders(b bool) Option

WithIncludeHeaders, when true, attaches the response headers on each returned Response (as Response.ResponseHeaders). Default: false.

func WithLogger

func WithLogger(l Logger) Option

WithLogger attaches a logger that receives diagnostic messages (one line per request attempt, retry, and backoff). The API key is always redacted from logged URLs.

func WithMaxRetries

func WithMaxRetries(n int) Option

WithMaxRetries sets the total number of request attempts (1 = no retry). Retries cover network errors, HTTP 429, and 5xx responses. Auth and other 4xx errors are never retried. Default: 5.

func WithPaginationDelay

func WithPaginationDelay(d time.Duration) Option

WithPaginationDelay sets the delay between page requests when using ScrollAll or Paginate. Default: 1s.

func WithRetryBackoff

func WithRetryBackoff(d time.Duration) Option

WithRetryBackoff sets the base for exponential backoff between retries. Default: 2s. Successive attempts wait 2s, 4s, 8s, 16s, 32s (capped by WithRetryBackoffMax).

func WithRetryBackoffMax

func WithRetryBackoffMax(d time.Duration) Option

WithRetryBackoffMax caps the longest single backoff sleep. Default: 60s.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets a per-request timeout. Applied via http.Client.Timeout, so it covers the full request (connect + body). Default: 30s.

type Params

type Params map[string]any

Params is the request payload for any endpoint method. Keys are case- insensitive (the API normalises to lowercase, and so do we) and values may be a string, a []string (comma-joined), a bool, an int, a float, or *bool / *float64 if you need to distinguish "unset" from a zero value.

A few keys are interpreted specially and never sent to the API:

"rawQuery" (string)       — sent verbatim; mutually exclusive with all other
                            params; parsed and validated against the
                            endpoint's allowed keys.

See the README for the per-endpoint accepted parameter list.

type Response

type Response struct {
	Status          string          `json:"status,omitempty"`
	TotalResults    int             `json:"totalResults,omitempty"`
	Results         json.RawMessage `json:"results,omitempty"`
	NextPage        string          `json:"nextPage,omitempty"`
	ResponseHeaders http.Header     `json:"-"`
}

Response is the top-level JSON envelope returned by every endpoint.

`Results` is held as raw JSON because its type varies by endpoint:

  • news endpoints (latest, archive, crypto, market) return an array of articles;
  • the count endpoints return an aggregate object on the final page.

Use the Articles() / Aggregate() helpers to decode it into a typed shape.

func (*Response) Aggregate

func (r *Response) Aggregate() (map[string]any, error)

Aggregate decodes Results as a map (the shape count endpoints return). Returns nil, nil when Results is empty or not an object.

func (*Response) Articles

func (r *Response) Articles() ([]Article, error)

Articles decodes Results as []Article. Returns nil, nil when Results is empty or not an array (e.g. on a count endpoint's aggregate page).

Directories

Path Synopsis
examples
error_handling command
Typed error handling with errors.As — same pattern as the Python / Node clients but using Go's idiomatic error chain.
Typed error handling with errors.As — same pattern as the Python / Node clients but using Go's idiomatic error chain.
latest command
A minimal example: fetch the latest news with a few filters.
A minimal example: fetch the latest news with a few filters.
paginate command
Two flavours of pagination:
Two flavours of pagination:

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
Morty Proxy This is a proxified and sanitized view of the page, visit original site.