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"
}
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
| Market | Board | Source |
|---|---|---|
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.
| Original | Here |
|---|---|
GET /api/price/currency | Same path, same body |
GET /api/price/gold | Same 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.