Although stock data APIs are becoming more commonplace, it is rare to find one that supports Go. This is why Market Data set out to build our own Golang Stock API with a focus on rapid development and ease of use. Our Go SDK’s API has extensive documentation, examples for every endpoint, and is easy to get up and running.
This tutorial takes you from an empty directory to a printed stock quote. It is a handful of commands and about twenty lines of Go.
Setting Up Your Environment
To set up your environment for using the Market Data Go SDK, follow these steps:
- Sign Up for a Market Data Account: Visit Market Data to sign up for a free account. This account gives you access to the API, allowing you to explore its capabilities without any cost. You can also opt for a paid plan with a 30-day free trial to explore premium features.
- Set Up Authentication: After signing up, you’ll receive an API token via email. Set this token as an environment variable named
MARKETDATA_TOKENto authenticate your requests to the Market Data API. This can be done in your terminal:- For macOS/Linux:
export MARKETDATA_TOKEN="<your_api_token>" - For Windows:
setx MARKETDATA_TOKEN "<your_api_token>"
- For macOS/Linux:
- Install the SDK: Use the Go package manager to install the SDK. The
/v2is part of the module path itself, so it appears in thego getcommand and in every import:go get github.com/MarketDataApp/sdk-go/v2 - Import the SDK in Your Project: The client lives in the
marketdatapackage, and each group of endpoints has a package of its own beside it:import "github.com/MarketDataApp/sdk-go/v2/marketdata"
From an empty directory, that is three commands:
mkdir quote-demo && cd quote-demo
go mod init quote-demo
go get github.com/MarketDataApp/sdk-go/v2The SDK requires Go 1.22 or later, and it pulls in no third-party dependencies: everything it needs at runtime is in the Go standard library.
Fetching a Real-Time Stock Quote With The Golang Stock API
If you’ve used our API before, you’re probably already familiar with getting live stock quotes from our stock quote endpoint. Using the Go SDK is just as easy. Save this as main.go in the directory you just created:
package main
import (
"context"
"fmt"
"log"
"github.com/MarketDataApp/sdk-go/v2/marketdata"
)
func main() {
// NewClient reads MARKETDATA_TOKEN from the environment or a .env file.
client, err := marketdata.NewClient()
if err != nil {
log.Fatalf("Failed to create the client: %v", err)
}
defer client.Close()
ctx := context.Background()
// Every method takes a context first and returns three values: the data,
// the response metadata, and an error.
quote, resp, err := client.Stocks.Quote(ctx, "AAPL")
if err != nil {
log.Fatalf("Failed to get the stock quote: %v", err)
}
// A quote describes a single thing, so the SDK returns a nil pointer when
// the API had no data. Check it before you read a field.
if quote == nil {
log.Fatal("No quote data was available for AAPL.")
}
fmt.Println(quote)
fmt.Printf("Credits remaining: %d\n", resp.RateLimit.Remaining)
}Run it with go run ., and put your own symbol in place of AAPL. Two things in that program are worth naming, because every other call you make will have the same shape.
The middle return value is the response metadata. It carries the rate-limit headers for that exact request, a NoData flag, and the raw *http.Response. Discard it with _ whenever you do not need it.
The fmt.Println(quote) works because every response type implements fmt.Stringer. You get a readable one-line summary without writing a format string:
AAPL Last: $302.77 Bid: 302.75 (2) Ask: 302.79 (3) Mid: 302.77 Chg: -0.65 (-0.21%) Vol: 41203110 Updated: 2026-08-04 10:15:04The snippets in the rest of this tutorial assume the client and the ctx from that program.
Two Ways to Call Every Endpoint
Every method comes in two forms. The context-first form above is the one to use in a real program. Beside it is a Get-prefixed convenience wrapper that supplies a background context and returns only the data and the error:
quote, err := client.Stocks.GetQuote("AAPL")
if err != nil {
log.Fatalf("Failed to get the stock quote: %v", err)
}
fmt.Println(quote)Use GetQuote for a script or a quick experiment. Use Quote when you want a deadline, cancellation, or the rate-limit numbers.
Fetching Many Symbols At Once
The plural method takes a slice, and the whole set costs one API request:
quotes, _, err := client.Stocks.Quotes(ctx, []string{"AAPL", "MSFT", "GOOG"})
if err != nil {
log.Fatalf("Failed to get the stock quotes: %v", err)
}
for _, q := range quotes {
fmt.Printf("%s: $%.2f (%.2f%%)\n", q.Symbol, q.Last, q.ChangePercent*100)
}Working with Stock Quotes in Go
Understanding the stock data returned by the Go SDK involves getting familiar with the Quote struct, which is central to handling stock data in the Market Data Go SDK. It lives in the stocks package, so you will see it written as stocks.Quote, and it encapsulates the details of a full level 1 stock quote.
type Quote struct {
Symbol string // Symbol is the stock ticker symbol.
Ask float64 // Ask is the current ask price.
AskSize int // AskSize is the size of the ask.
Bid float64 // Bid is the current bid price.
BidSize int // BidSize is the size of the bid.
Mid float64 // Mid is the midpoint between bid and ask.
Last float64 // Last is the last trade price.
Change float64 // Change is the price change from the previous close.
ChangePercent float64 // ChangePercent is the FRACTIONAL change: -0.0021 means -0.21%.
Volume int64 // Volume is the trading volume.
Updated time.Time // Updated is when the quote was last updated, in US/Eastern.
FiftyTwoWeekHigh float64 // Requested with stocks.WithFiftyTwoWeek(true).
FiftyTwoWeekLow float64 // Requested with stocks.WithFiftyTwoWeek(true).
Open float64 // Session OHLC, requested with stocks.WithCandle(true).
High float64
Low float64
Close float64
}Interpreting the Quote Struct
When interpreting the data fields, it’s important to consider the context of the stock market and the specific stock you’re analyzing. For example, a significant change in the Ask and Bid prices could indicate a shift in market sentiment, while the Volume can give insights into the stock’s liquidity. The FiftyTwoWeekHigh and FiftyTwoWeekLow provide a perspective on the stock’s performance over the past year, and the Change and ChangePercent offer immediate feedback on recent price movements.
Two details of the struct will catch you out if you skip them:
ChangePercentis a fraction, not a percentage. The API sends-0.0021to mean −0.21%, and the SDK passes it through unchanged. Multiply by 100 before you print it. TheString()method already does this for you.- The 52-week fields are empty unless you ask for them. They stay at zero unless the request carried
stocks.WithFiftyTwoWeek(true), and that option exists only on the single-symbolQuotemethod — the bulk quotes endpoint does not supply the data.
Every timestamp the SDK returns is normalized to US/Eastern, the timezone of the US exchanges, so you do not have to convert Updated before comparing it to a market session.
The struct also carries two helper methods, so you do not have to work the arithmetic out yourself:
fmt.Printf("Spread: %.2f (%.2f%% of the mid)\n", quote.Spread(), quote.SpreadPercent())To fetch the 52-week range, pass the option:
quote, _, err := client.Stocks.Quote(ctx, "AAPL", stocks.WithFiftyTwoWeek(true))
if err != nil {
log.Fatalf("Failed to get the stock quote: %v", err)
}
if quote != nil {
fmt.Printf("52-week range: %.2f - %.2f\n", quote.FiftyTwoWeekLow, quote.FiftyTwoWeekHigh)
}That option comes from the stocks package, so add it to your imports when you use one:
import "github.com/MarketDataApp/sdk-go/v2/marketdata/stocks"Handling Errors and Missing Data
The SDK returns typed errors that work with errors.Is and errors.As, and it separates two situations that both arrive from the API as an HTTP 404.
A symbol the API does not recognize is an error, so a typo fails loudly instead of reading as an empty answer:
quote, resp, err := client.Stocks.Quote(ctx, "ZZZZQQ")
if err != nil {
if errors.Is(err, marketdata.ErrNotFound) {
log.Fatal("That symbol does not exist.")
}
// Errors the API produced carry a support block you can paste into a ticket.
var apiErr *marketdata.APIError
if errors.As(err, &apiErr) {
log.Fatal(apiErr.SupportInfo())
}
log.Fatalf("Failed to get the stock quote: %v", err)
}A valid question with an empty answer is not an error. The error is nil, resp.NoData is true, and the result is empty rather than absent. Methods that return a slice give you an empty slice, which is safe to range over. Quote returns a nil pointer, because a zero-valued quote would read as a real price of zero:
if quote == nil {
fmt.Println("The request succeeded, but the API had no quote to return.")
fmt.Println("NoData:", resp.NoData)
}Rate limiting has a sentinel of its own, so you can back off rather than retry blindly:
if errors.Is(err, marketdata.ErrRateLimited) {
fmt.Println("Out of credits for now.")
}Two numbers tell you where you stand. resp.RateLimit is exact and describes the request you just made; client.RateLimits() is a running snapshot of the last completed response, which is convenient for monitoring but may lag when requests run concurrently.
limits := client.RateLimits()
fmt.Printf("Credits: %d of %d remaining, resets at %v\n",
limits.Remaining, limits.Limit, limits.ResetAt)Configuring the Client
NewClient needs no arguments, because it reads MARKETDATA_TOKEN from the environment. It also reads a .env file in your working directory, and a real environment variable always wins over the file.
To pass the token yourself, or to change any other setting, use the functional options:
client, err := marketdata.NewClient(
marketdata.WithToken("<your_api_token>"),
marketdata.WithMaxRetries(3),
marketdata.WithDebug(true),
)
if err != nil {
log.Fatalf("Failed to create the client: %v", err)
}
defer client.Close()Create one client and pass it around: it is safe to share across goroutines, and it limits itself to 50 requests in flight at a time. Give it a defer client.Close() so it releases its resources when your program ends.
Going Further: Historical Candles
Quotes are one endpoint of many, and the rest follow the same shape. Historical candles show the other half of the design — optional parameters arrive as functional options:
candles, _, err := client.Stocks.Candles(ctx, "AAPL",
stocks.WithResolution(stocks.ResolutionDaily),
stocks.WithCandleWindow(stocks.LastN(30)),
)
if err != nil {
log.Fatalf("Failed to get the candles: %v", err)
}
for _, c := range candles {
fmt.Println(c)
}WithCandleWindow takes one value that describes the whole date range, built from a time.Time. LastN(30) asks for the 30 most recent periods; Between(from, to), Since(from), Until(to), OnDate(day) and LastNUntil(n, to) are the other ways to say it.
The window being a single value is deliberate. The API’s date parameters are mutually exclusive, and that exclusivity is enforced by the compiler: a date range plus a countback is not something you can write. An illegal combination fails to build rather than failing at runtime with a message from the server.
Beyond stocks, the client groups the rest of the API into client.Options, client.Funds, client.Markets and client.Utilities. They all work the way the calls above do.
Additional Resources
You can consult the Market Data Go SDK on Github as well as access the SDK’s documentation on pkg.go.dev. The repository also ships runnable examples, from a copy-paste quick start to full-screen terminal apps that between them exercise every SDK method. However, we recommend using our own Go SDK Documentation Portal for detailed information and examples.
