The official Go client for source-timestamped oil, gas, refined-product, futures, and related energy data from OilPriceAPI.
Get an API key | Documentation | API explorer | Pricing
- Go 1.21 or newer
- API base URL:
https://api.oilpriceapi.com - Auth header:
Authorization: Token YOUR_API_KEY - Environment variable used by the executable example:
OILPRICEAPI_KEY
go get github.com/OilpriceAPI/oilpriceapi-goThe canonical authenticated first request is:
GET /v1/prices/latest?by_code=BRENT_CRUDE_USD
Run the repository's tested example:
export OILPRICEAPI_KEY="your-api-key"
go run github.com/OilpriceAPI/oilpriceapi-go/example@latestThe same request in application code:
package main
import (
"context"
"fmt"
"log"
"os"
oilpriceapi "github.com/OilpriceAPI/oilpriceapi-go"
)
func main() {
client := oilpriceapi.NewClient(os.Getenv("OILPRICEAPI_KEY"))
response, err := client.GetLatestPrices(
context.Background(),
oilpriceapi.WithCommodity("BRENT_CRUDE_USD"),
)
if err != nil {
log.Fatal(err)
}
price := response.Data.Prices[0]
fmt.Printf("%s %.2f %s/%s as of %s\n",
price.Code, price.Price, price.Currency, price.Unit, price.UpdatedAt)
}Production returns a singleton data object for this endpoint. The SDK
normalizes that object to one entry in response.Data.Prices. It also accepts
the legacy data.prices[] response for backward compatibility and rejects a
successful response that contains no usable price.
For missing configuration and actionable 401, 403, and 429 recovery, use the
exact executable source in example/main.go. CI copies that
file into a clean consumer module and runs every recovery path against fixtures.
by_code accepts up to 20 comma-separated commodity codes, and the whole call
counts as one request — not one per code. Batching is the cheapest way to make
an allowance go further: twenty codes in one call stretches it twenty times.
resp, err := client.GetLatestPrices(ctx,
oilpriceapi.WithCommodity("BRENT_CRUDE_USD,WTI_USD,NATURAL_GAS_USD"))
if err != nil {
log.Fatal(err)
}
for _, p := range resp.Data.Prices {
fmt.Printf("%s %.2f %s\n", p.Code, p.Price, p.Currency)
}One code returns a single price; two or more populate Data.Prices.
Asking for more than 20 codes returns 400 Too many commodity codes requested (max: 20, requested: N). An unrecognised code also returns 400, with a "did you
mean" suggestion — so validate your code list once rather than on every poll.
For current plan allowances and the polling interval that fits them, see Rate Limiting.
The demo endpoint does not require an API key:
client := oilpriceapi.NewClient("")
response, err := client.GetDemoPrices(context.Background())
if err != nil {
log.Fatal(err)
}
for _, price := range response.Data.Prices {
fmt.Printf("%s %.2f %s/%s\n",
price.Code, price.Price, price.Currency, price.Unit)
}Demo availability and limits are returned by the endpoint. Authenticated dataset access and limits vary by plan, source, and account entitlement.
client := oilpriceapi.NewClient(apiKey,
oilpriceapi.WithTimeout(15*time.Second),
oilpriceapi.WithRetries(2),
)WithBaseURL and WithHTTPClient are available for testing and custom network
configuration. All client methods accept context.Context.
| Use case | SDK method |
|---|---|
| Latest price | GetLatestPrices |
| Historical prices | GetHistoricalPrices |
| Commodity catalog | GetCommodities |
| Futures and curves | GetFuturesLatest, GetFuturesCurve |
| Forecasts | GetForecasts |
| Storage and rig counts | GetStorage, GetRigCounts |
| Marine fuels and drilling | GetMarineFuels, GetDrillingSummary |
| Well production | client.WellProduction() |
| Alerts | GetAlerts, CreateAlert, UpdateAlert, DeleteAlert |
| Webhooks | ListWebhooks, CreateWebhook, UpdateWebhook, DeleteWebhook |
| Analytics | GetAnalyticsPerformance, GetAnalyticsCorrelation, GetAnalyticsTrend, GetAnalyticsForecast |
| Energy intelligence | client.EI() |
| Market brief | GetMarketBrief |
| Agent subscriptions | GetSubscriptions, CreateSubscription, GetSubscriptionEvents, DeleteSubscription |
| WebSocket stream | StreamPrices |
Use the Go package reference for method parameters and response types. Availability varies by dataset, plan, source, and account entitlement; the current source is pricing.
summary, err := client.WellProduction().GetSummary(ctx)
if err != nil {
log.Fatal(err)
}
if summary.Data.Coverage == nil {
log.Fatal("well-production coverage is unavailable")
}
covered := make(map[string]bool)
for _, state := range summary.Data.Coverage.WellLevelStatesWithData {
covered[state] = true
}
permits, err := client.EI().SearchWellPermits(ctx, oilpriceapi.WellPermitSearchQuery{
States: "TX",
WellName: "Eagle",
})
if err != nil {
log.Fatal(err)
}
isAPI14 := func(value string) bool {
if len(value) != 14 {
return false
}
for _, digit := range value {
if digit < '0' || digit > '9' {
return false
}
}
return true
}
for _, permit := range permits.WellPermits {
if !covered[permit.StateCode] || !isAPI14(permit.APINumber) {
continue
}
production, err := client.WellProduction().GetWellDetail(ctx, permit.APINumber)
if err != nil {
log.Print(err)
continue
}
fmt.Println(permit.Well.Name, production.Data.Data)
}The accessor also supports state summaries, state and well history, top producers, and cycle-time analysis. Check the returned coverage metadata before treating a state or well-level result as complete.
response, err := client.GetHistoricalPrices(ctx, "BRENT_CRUDE_USD",
oilpriceapi.WithPeriod("week"),
)For a custom range, use WithStartDate, WithEndDate, and WithInterval.
response, err := client.GetFuturesLatest(ctx,
oilpriceapi.WithContract("brent"),
)
if err == nil && response.FrontMonth != nil {
fmt.Printf("%s %.2f\n",
response.FrontMonth.ContractMonth,
response.FrontMonth.LastPrice,
)
}WithContract accepts the API slug or a supported short code such as BZ or
CL. The SDK keeps unknown slugs forward-compatible.
response, err := client.GetLatestPrices(ctx,
oilpriceapi.WithCommodity("BRENT_CRUDE_USD"),
)
if err != nil {
var authErr *oilpriceapi.AuthenticationError
var rateErr *oilpriceapi.RateLimitError
var apiErr *oilpriceapi.APIError
switch {
case errors.As(err, &authErr):
log.Print("replace OILPRICEAPI_KEY with an active key")
case errors.As(err, &rateErr):
log.Printf("retry after %d seconds", rateErr.RetryAfter)
case errors.As(err, &apiErr) && (apiErr.StatusCode == 402 || apiErr.StatusCode == 403):
log.Print("review dataset access at https://www.oilpriceapi.com/pricing")
default:
log.Printf("request failed: %v", err)
}
}The SDK also exposes NotFoundError, ServerError, and
StreamRejectedError.
Use Raw for a versioned API route that does not yet have a typed method:
var result map[string]any
err := client.Raw(ctx, http.MethodGet, "/v1/some/versioned/route",
url.Values{"days": {"30"}}, &result)StreamPrices opens an ActionCable WebSocket and returns typed updates.
Check stream.Err() after Updates() closes. Availability varies by account
entitlement; a rejected subscription returns StreamRejectedError with a
recovery link.
Source timestamps describe the values in each response. They do not imply one sitewide update interval: refresh cadence varies by source, market hours, dataset, and plan.
The versioned, reviewed contract is
product-facts.json. Mutable
offer, catalog, freshness, entitlement, and data-rights claims should link to
that contract instead of being copied into SDK documentation.
Current reviewed catalog wording: a broad catalog spanning crude oil, natural gas, refined products, futures, marine fuels, carbon markets, metals, forex, and selected energy-intelligence datasets. See the commodity catalog for current availability.
Standard plans provide API access, normalization, monitoring, and delivery; they do not grant ownership of source data or unrestricted raw-data redistribution rights. See the data usage policy.
go test ./...
go test -race ./...
go vet ./...
./scripts/clean-install-smoke.shThe guarded production suite requires OILPRICEAPI_TEST_KEY:
OILPRICEAPI_TEST_KEY="your-test-key" go test -tags live -run TestLiveGetLatestPrice ./...MIT licensed. See LICENSE.