documentation/ingestion/clients/go.md
import { ILPClientsTable } from "@theme/ILPClientsTable"
QuestDB supports the Go ecosystem, offering a Go client designed for high-performance data ingestion, tailored specifically for insert-only operations. This combination of QuestDB and its Go client provides exceptional time series data ingestion and analytical capabilities.
The Go client introduces several advantages:
This quick start guide will help you get up and running with the basic functionalities of the Go client, covering connection setup, authentication, and some common insert patterns.
<ILPClientsTable language="Golang" />:::info
This page focuses on our high-performance ingestion client, which is optimized for writing data to QuestDB. For retrieving data, we recommend using a PostgreSQL-compatible Go library or our HTTP query endpoint.
:::
To add the QuestDB client to your Go project:
go get github.com/questdb/go-questdb-client/
Passing in a configuration string with HTTP basic authentication:
package main
import (
"context"
"github.com/questdb/go-questdb-client/v4"
)
func main() {
ctx := context.TODO()
client, err := questdb.LineSenderFromConf(ctx, "http::addr=localhost:9000;username=admin;password=quest;")
if err != nil {
panic("Failed to create client")
}
// Utilize the client for your operations...
}
Or, set the QDB_CLIENT_CONF environment variable and call
questdb.LineSenderFromEnv().
export QDB_CLIENT_CONF="http::addr=localhost:9000;username=admin;password=quest;"
client, err := questdb.LineSenderFromEnv(context.TODO())
Alternatively, you can use the built-in Go API to specify the connection options.
package main
import (
"context"
qdb "github.com/questdb/go-questdb-client/v4"
)
func main() {
ctx := context.TODO()
client, err := qdb.NewLineSender(context.TODO(), qdb.WithHttp(), qdb.WithAddress("localhost:9000"), qdb.WithBasicAuth("admin", "quest"))
When using QuestDB Enterprise, authentication can also be done via REST token. Please check the RBAC docs for more info.
Example: inserting executed trades for cryptocurrencies.
Without authentication and using the current timestamp:
package main
import (
"context"
"github.com/questdb/go-questdb-client/v4"
)
func main() {
ctx := context.TODO()
client, err := questdb.LineSenderFromConf(ctx, "http::addr=localhost:9000;")
if err != nil {
panic("Failed to create client")
}
err = client.Table("trades").
Symbol("symbol", "ETH-USD").
Symbol("side", "sell").
Float64Column("price", 2615.54).
Float64Column("amount", 0.00044).
AtNow(ctx)
if err != nil {
panic("Failed to insert data")
}
err = client.Flush(ctx)
if err != nil {
panic("Failed to flush data")
}
}
In this case, the designated timestamp will be the one at execution time. Let's see now an example with an explicit timestamp, custom auto-flushing, and basic auth.
package main
import (
"context"
"github.com/questdb/go-questdb-client/v4"
"time"
)
func main() {
ctx := context.TODO()
client, err := questdb.LineSenderFromConf(ctx, "http::addr=localhost:9000;username=admin;password=quest;auto_flush_rows=100;auto_flush_interval=1000;")
if err != nil {
panic("Failed to create client")
}
timestamp := time.Now()
err = client.Table("trades").
Symbol("symbol", "ETH-USD").
Symbol("side", "sell").
Float64Column("price", 2615.54).
Float64Column("amount", 0.00044).
At(ctx, timestamp)
if err != nil {
panic("Failed to insert data")
}
err = client.Flush(ctx)
// You can flush manually at any point.
// If you don't flush manually, the client will flush automatically
// when a row is added and either:
// * The buffer contains 75000 rows (if HTTP) or 600 rows (if TCP)
// * The last flush was more than 1000ms ago.
// Auto-flushing can be customized via the `auto_flush_..` params.
if err != nil {
panic("Failed to flush data")
}
}
We recommended to use User-assigned timestamps when ingesting data into QuestDB. Using the current timestamp hinder the ability to deduplicate rows which is important for exactly-once processing.
<!-- ## Decimal insertion :::note Decimals are available when ILP protocol version 3 is active (QuestDB 9.2.0+). The HTTP sender negotiates v3 automatically; with TCP add `protocol_version=3;` to the configuration string. ::: :::caution Create the target decimal columns ahead of ingestion with an explicit `DECIMAL(precision, scale)` definition so QuestDB knows how many digits to keep. See the [decimal data type](/docs/query/datatypes/decimal/#creating-tables-with-decimals) page for details on precision and scale. ::: QuestDB decimal columns accept either validated string literals or pre-scaled binary payloads. The text path keeps things simple and lets the server parse the literal while preserving the scale you send: ```go err = sender. Table("quotes"). Symbol("ccy_pair", "EURUSD"). DecimalColumnFromString("mid", "1.234500"). AtNow(ctx) ``` `DecimalColumnFromString` checks the literal (digits, optional sign, decimal point, exponent, `NaN`/`Infinity`) and appends the d suffix that the ILP parser expects, so the value above lands with scale = 6. For full control or when you already have a fixed-point value, build a `questdb.Decimal` and use the binary representation. The helpers keep you inside QuestDB’s limits (scale ≤ 76, unscaled payload ≤ 32 bytes) and avoid server-side parsing: ```go price := qdb.NewDecimalFromInt64(12345, 2) // 123.45 with scale 2 commission, err := qdb.NewDecimal(big.NewInt(-750), 4) if err != nil { log.Fatal(err) } err = sender. Table("trades"). Symbol("symbol", "ETH-USD"). DecimalColumn("price", price). DecimalColumn("commission", commission). AtNow(ctx) ``` If you already hold a two’s complement big-endian mantissa (for example, from another fixed-point library) call `NewDecimalUnsafe(rawBytes, scale)`, passing nil encodes a NULL and the client skips the field. The client also understands [github.com/shopspring/decimal](https://github.com/shopspring/decimal) values: ```go dec := decimal.NewFromFloat(2615.54) err = sender. Table("trades"). DecimalColumnShopspring("price", dec). AtNow(ctx) ``` `DecimalColumnShopspring` converts the coefficient/exponent pair into the same binary payload, so you can reuse existing business logic while still benefiting from precise wire formatting. -->The minimal configuration string needs to have the protocol, host, and port, as in:
http::addr=localhost:9000;
In the Go client, you can set the configuration options via the standard config string, which is the same across all clients, or using the built-in API.
For all the extra options you can use, please check the client docs
Alternatively, for a breakdown of Configuration string options available across all clients, see the Configuration string page.
Please refer to the ILP overview for details about transactions, error control, delivery guarantees, health check, or table and column auto-creation.
Explore the full capabilities of the Go client via Go.dev.
With data flowing into QuestDB, now it's time to for analysis.
To learn The Way of QuestDB SQL, see the Query & SQL Overview.
Alone? Stuck? Want help? Visit us in our Community Forum.