Go · MIT · one dependency

Live Iranian market prices,
as a library and a service

tgju-api-go reads the currency, gold and coin boards published by tgju.org and hands them back as ordinary Go values — or as JSON over HTTP, from a container you run yourself.

Both halves, one codebase

The same client backs the import and the endpoint, so the two can never disagree about what a price is. Pick whichever hop you would rather not pay for.

As a Go module

go get github.com/amiranmanesh/tgju-api-go
client := tgju.New()

snap, err := client.Gold(ctx)
if err != nil {
    return err
}

item, _ := snap.Lookup("geram18")
fmt.Println(item.Title, item.Price.Toman())

No HTTP hop, no sidecar, no second process to operate.

As a service

docker run -p 8080:8080 \
  ghcr.io/amiranmanesh/tgju-api-go:latest
curl localhost:8080/v1/markets/gold
curl localhost:8080/v1/items/price_dollar_rl
curl localhost:8080/api/price/currency

Or mount it inside a service you already run: mux.Handle("/prices/", http.StripPrefix("/prices", server.New(client)))

What you get back

Every amount carries the site's own rendering next to the parsed number, so you can show one and compute with the other without writing a parser of your own.

{
  "key": "price_dollar_rl",
  "title": "دلار",
  "market": "currency",
  "category": "عنوان",
  "price": { "text": "1,864,000", "value": 1864000 },
  "low":   { "text": "1,860,800", "value": 1860800 },
  "high":  { "text": "1,869,100", "value": 1869100 },
  "change": {
    "status": "low",
    "percent": 0.32,
    "amount": { "text": "6,050", "value": 6050 }
  },
  "time": "11:49:45",
  "profile_url": "https://www.tgju.org/profile/price_dollar_rl"
}
Prices are quoted in rial, as tgju quotes them. Amount.Toman() converts; the CLI takes --unit toman.

Designed for a service that has to stay up

Requests collapse

Snapshots are cached for a short window, and concurrent misses for the same board become one request upstream — not one per caller.

Failures are typed

“tgju is down” and “tgju changed its markup” are different errors with different HTTP statuses. One is worth a retry; the other is worth a release.

Column order is read, not assumed

The parser follows the table headers, so a reshuffle upstream cannot silently swap the daily low with the daily high.

Persian numerals handled

Persian and Arabic-Indic digits, thousands separators and zero-width joiners are normalised before anything is parsed.

One dependency

golang.org/x/net/html, and nothing else. No web framework, no metrics client, no YAML parser.

Operable out of the box

Liveness and readiness probes, Prometheus metrics, request IDs, structured logs, a rate limiter and a scratch-based image that runs as non-root.

Markets

MarketBoardSource
currencyارزtgju.org/currency
goldطلا و نقرهtgju.org/gold-chart
coinسکهtgju.org/coin

The crypto board is built by client-side JavaScript and is deliberately absent: scraping it would need a browser, and a browser has no place in a library.

Moving from the Python service

This project reimplements BlackIQ/tgju-api. Its response shape is preserved under /api/price/*, byte for byte, so an existing client changes one hostname and nothing else.

OriginalHere
GET /api/price/currencySame path, same body
GET /api/price/goldSame path, same body
GET /api/price/coin, the same shape for coins
GET /v1/… with parsed numbers, the change and the timestamp

Where to go next

API reference

Every endpoint, parameter and response, rendered from the OpenAPI document the server itself serves.

Package documentation

The Go API, with runnable examples.

Wiki

Deployment, configuration, the instrument key catalogue, and how to add a market.

openapi.yaml

The raw document, for generating a client.