Skip to content

Commit 81807f2

Browse files
feat: add a Go SDK alongside the Python and Node.js ports (#119)
* feat: add a Go SDK alongside the Python and Node.js ports The port carries the same features as the other two, with the surface shaped for Go: a context on every request, errors instead of exceptions, Options instead of keyword arguments, and a zero value that means the default the other ports declare in their signatures. Two pieces have no counterpart in the sibling ports, because their HTTP clients provide them: - a cookie jar that honours the scope FR24 sets, so a cookie stored by www. is not replayed to cdn./api./data-live., and a single cookie can be dropped to shed load-balancer stickiness without losing the login - content decoding owned by the package (gzip, deflate, brotli), which is what makes the size budget enforceable before a body expands Parity is enforced rather than promised: ports_test.go reads the Python source and fails when the country list, the zones, the tracker config, the flight attributes or the client methods drift apart. * ci: run, release and label the Go port The offline suite gates pull requests and the live FR24 suite runs with retries, mirroring the Python and Node.js workflows. gofmt, go vet and staticcheck stand in for flake8/mypy and eslint/tsd, and a consumer module is built against the package so a broken public surface fails the build. Releases need no registry upload: for Go the tag is the release. Since the module lives in a subdirectory, the toolchain only sees tags carrying that prefix, so publish.yml now pushes go/vX.Y.Z alongside the release tag, and the version check covers all three ports. The labeler tells the Go tests apart from the package itself, which matters because Go keeps them in the same directory where python/ and nodejs/ keep a sibling tests/ folder. * docs: list Go across the project docs The per-package READMEs now describe only their own port, and the project-wide pages list all three. The issue templates asked for a "Python Version", which was already wrong for Node.js. * docs: trim the Go readme to the shape of its siblings The package readme now carries what the Python and Node.js ones do — install, basic usage, documentation link — and nothing else. The deeper material it held (entity constructors, client options, error handling, TLS impersonation, the differences table) moves to docs/go.md, where the equivalent Python and Node.js pages already live. * docs: carry the Go badges in every readme Each readme leads with its own workflow badge and then repeats the project-wide block, so the Go one was missing Pypi, Npm, Downloads and Frequency, and the other three were missing the Go reference and version. The project-wide pages listed only some of the workflow badges: the root readme had no Node.js one and the documentation home had neither Node.js nor Go. * docs: add a Node version badge and group the license with the workflows Each registry badge now sits next to the version it needs — Pypi with Python, Npm with Node, the Go reference with Go — and the license moves up beside the workflow badges. The Node floor is the engines.node of nodejs/package.json. * fix: harden the edges a public surface exposes Eight findings from the review on the pull request, each reproduced before it was changed: - CheckInfo reported an unknown field only when Go's randomised map iteration put it before a criterion that fails: 26 of 200 runs on the same input. Every criterion is now validated before any is evaluated. - GetFlightDetails and GetHistoryData take a pointer, so nil is valid at compile time and panicked instead of returning an error. - A body with data spliced after the JSON was accepted, where the Python and Node.js parsers reject it: Decode reads the first value only. - Network failures did not wrap ErrFlightRadar, contradicting the taxonomy documented on that sentinel. They now wrap both it and the transport's own cause, which the retry policy still reads. - A negative Client.Timeout skipped the deadline entirely instead of falling back to the default the field documents. - New() panicked when the process had replaced http.DefaultTransport, which mocking libraries legitimately do. - SleepFor panicked on the largest Jitter a Duration can hold, and could wrap round when adding it to the delay. - Missing aircraft images stayed nil where the other ports default to an empty list, which changes both a CheckInfo comparison and the JSON. * fix: retry a body lost mid-download, and widen two value readers A connection dropped partway through a response was classified as permanent, because the body is read after Do returns and so carries no *url.Error of its own: against a server that promises 5000 bytes and hangs up after 6, a policy asking for three attempts made one. The Python port retries the same failure, where curl_cffi reads the body inside the call. The read failure is now wrapped the way the transport wraps its own. Two readers were narrower than they read: nativeNumber covered every numeric kind except float64, so a defined type over float64 was refused where one over int64 worked, and getString ignored json.Number, which this package itself produces when it decodes with UseNumber. * fix: send every in-scope cookie, and never sleep on a bad delay The jar collapsed same-named cookies to one, so a request to /data/... carried the root token where FR24 had scoped a different value to that path. RFC 6265 5.4 asks for every match, longest path first and oldest first among equal paths, which is also what the cookie jar behind the Python port does. get() still answers with the newest re-issue: it says which token is current, not what to put on the wire. A negative BaseDelay reached the overflow guard added with the last round of fixes and came back as the longest sleep a Duration can hold — 292 years before the first retry. The zero BaseDelay that turns into NaN once the doubling overflows landed in the same place. * test: fold two duplicated bounds tests into the parity one GetBounds was asserted twice with the same call and different constants, and the "box surrounds the point" test was subsumed by the one comparing all four values against the numbers the Python suite pins. Its two assertions moved into the survivor rather than being dropped: they are what catches an expected-value fixture updated the wrong way. Reordering the fields and "fixing" the fixture to match leaves the exact comparison passing and fails on the shape. * fix: keep the feed's order, and close four gaps the taxonomy left open GetFlights walked a Go map, whose iteration order is randomised, so the same feed answered in a different order on every call where the Python and Node.js ports keep the order FR24 sent. The keys are now read from the body itself. Four smaller ones, each reproduced first: - a body of "null" unmarshalled into a nil map with no error, so every key read as missing instead of the caller seeing the failure - Content-Type was matched case-sensitively, though a media type is not - Content-Encoding split across header fields decoded only its first layer and left the body compressed - cancellation during the retry backoff returned the bare context error, outside the taxonomy every other failure path follows A negative flightLimit, page or limit is no longer rewritten as the default: only zero selects it, and anything else goes to FR24 as given, which is what the sibling ports do. The workflow now also runs when python/FlightRadarAPI changes, since ports_test.go reads those files to detect drift and could not see a change that never triggered it. * fix: make an unset field mean the default, not an empty request SetFlightTrackerConfig copied the struct it was given without looking at it. Go's zero value is the empty string where the Python dataclass carries a default, so a literal {Limit: "10"} sent the feed adsb=&air=&estimated=&faa=… — while the very same empty value was rejected when it arrived through the values map. Unset fields now take the default and every field is validated, so the two paths agree. RetryPolicy read a zero MaxDelay as "no cap at all", which let a struct literal climb to a four-minute sleep where the Python constructor caps at thirty seconds. Zero now means the default the other ports declare, for BaseDelay as well, so &RetryPolicy{MaxAttempts: 5} backs off exactly like NewRetryPolicy(5). A zero Jitter still means none: a deterministic test wants to be able to ask for that. A cookie with Domain=localhost sent from localhost was discarded by the guard against a bare TLD, though RFC 6265 5.3.5 allows a domain that is the host itself. * chore: derive the accept-encoding header and pin the CI tools The header was written out while its comment claimed it came from the decoder table, so dropping a decoder would have left the client asking for an encoding it can no longer read — and the body would come back compressed, parsed as garbage rather than failing. It is now built from the table, with the order kept apart as the one part that is a choice. staticcheck and govulncheck ran as @latest inside a required CI step, the only tools in these workflows not pinned, so an upstream release could turn the build red on a tree nobody touched. Both are pinned in the Makefile, which the workflow now calls instead of repeating the command. Two comments had drifted from their code: the one on matching still described the one-cookie-per-name behaviour removed in 56c4fdf, and GetAirlineLogo carried an unreachable 5xx branch that read as though a server error fell through to the alternative URL.
1 parent 473aa17 commit 81807f2

43 files changed

Lines changed: 10698 additions & 34 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ If applicable, add screenshots to help explain your problem.
2626

2727
**System (please complete the following information):**
2828
- OS: [e.g. Windows]
29-
- Python Version [e.g. 1.10]
29+
- SDK: [Python, Node.js or Go]
30+
- Language version: [e.g. Python 3.12, Node.js 22, Go 1.26]
31+
- FlightRadarAPI version: [e.g. 1.6.0]
3032

3133
**Additional context**
3234
Add any other context about the problem here.

.github/ISSUE_TEMPLATE/questioning.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ If applicable, add screenshots to help explain your problem.
2929
If applicable, please complete the following information:
3030

3131
1. OS: [e.g. Windows]
32-
2. Python Version [e.g. 1.10]
32+
2. SDK: [Python, Node.js or Go]
33+
3. Language version: [e.g. Python 3.12, Node.js 22, Go 1.26]
34+
4. FlightRadarAPI version: [e.g. 1.6.0]
3335

3436
**Additional context**
3537
Add any other context about the problem here.

.github/dependabot.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ updates:
2323
- "deps"
2424
- "security"
2525

26+
- package-ecosystem: "gomod"
27+
directory: "/go"
28+
schedule:
29+
interval: "weekly"
30+
open-pull-requests-limit: 0
31+
labels:
32+
- "deps"
33+
- "security"
34+
2635
- package-ecosystem: "github-actions"
2736
directory: "/"
2837
schedule:

.github/workflows/go-package.yml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: Go Package
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- 'go/**'
9+
# ports_test.go reads these to keep the three SDKs aligned, so a change
10+
# there has to run this workflow or the drift goes unnoticed.
11+
- 'python/FlightRadarAPI/**'
12+
- '.github/workflows/go-package.yml'
13+
pull_request:
14+
paths:
15+
- 'go/**'
16+
- 'python/FlightRadarAPI/**'
17+
- '.github/workflows/go-package.yml'
18+
schedule:
19+
- cron: '0 0 */7 * *'
20+
workflow_dispatch:
21+
22+
defaults:
23+
run:
24+
working-directory: ./go
25+
26+
jobs:
27+
build:
28+
runs-on: ubuntu-latest
29+
strategy:
30+
fail-fast: false
31+
matrix:
32+
go-version: ['1.25.x', '1.26.x']
33+
steps:
34+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
35+
- name: Set up Go ${{ matrix.go-version }}
36+
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
37+
with:
38+
go-version: ${{ matrix.go-version }}
39+
cache-dependency-path: go/go.sum
40+
- name: Download dependencies
41+
run: |
42+
go mod download
43+
go mod verify
44+
- name: Check go.mod is tidy
45+
run: |
46+
go mod tidy
47+
git diff --exit-code -- go.mod go.sum
48+
- name: Lint
49+
run: make lint
50+
- name: Static analysis
51+
if: matrix.go-version == '1.26.x'
52+
run: make lint-strict
53+
- name: Offline tests (PR gate)
54+
# Coverage threshold lives in the Makefile ($(COVERAGE_MIN)); raise it
55+
# there and it applies here too.
56+
run: make test-coverage
57+
- name: Race detector
58+
run: make test-race
59+
- name: Integration tests (live FR24)
60+
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 # v3
61+
with:
62+
timeout_minutes: 10
63+
max_attempts: 3
64+
command: cd go && make test-integration
65+
continue-on-error: ${{ github.event_name == 'push' }}
66+
- name: Vulnerability scan (shipped dependencies)
67+
if: matrix.go-version == '1.26.x'
68+
run: make security
69+
- name: Verify the package builds as a dependency
70+
run: |
71+
go build ./...
72+
mkdir -p /tmp/consumer && cd /tmp/consumer
73+
go mod init consumer
74+
go mod edit -require=github.com/JeanExtreme002/FlightRadarAPI/go@v0.0.0
75+
go mod edit -replace=github.com/JeanExtreme002/FlightRadarAPI/go=$GITHUB_WORKSPACE/go
76+
cat > main.go <<'EOF'
77+
package main
78+
79+
import (
80+
"fmt"
81+
82+
"github.com/JeanExtreme002/FlightRadarAPI/go/flightradarapi"
83+
)
84+
85+
func main() {
86+
client := flightradarapi.New()
87+
fmt.Println("Install OK:", len(client.GetZones()), "zones")
88+
}
89+
EOF
90+
go mod tidy
91+
go run .

.github/workflows/labeler.yml

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,26 +34,46 @@ jobs:
3434
const paths = files.map((f) => f.filename);
3535
core.info(`Changed files (${paths.length}):\n ` + paths.join("\n "));
3636
37+
const goPackage = "go/flightradarapi/";
38+
39+
// Go keeps its tests inside the package, where python/ and nodejs/
40+
// keep them in a sibling tests/ directory. Told apart here so a
41+
// test-only PR gets "tests" and not "api".
42+
const isGoTest = (f) =>
43+
f.startsWith(goPackage) &&
44+
(/_test\.go$/.test(f) || f.startsWith(goPackage + "testdata/"));
45+
3746
const isCore = (f) =>
3847
f === "python/FlightRadarAPI/core.py" ||
3948
f === "python/FlightRadarAPI/request.py" ||
4049
f === "nodejs/FlightRadarAPI/core.js" ||
41-
f === "nodejs/FlightRadarAPI/request.js";
50+
f === "nodejs/FlightRadarAPI/request.js" ||
51+
f === "go/flightradarapi/core.go" ||
52+
f === "go/flightradarapi/request.go" ||
53+
// The Go port owns the cookie jar its siblings get from their
54+
// HTTP client, so it belongs to the same layer as request.go.
55+
f === "go/flightradarapi/cookies.go";
4256
4357
const isEntities = (f) =>
44-
/^(python|nodejs)\/FlightRadarAPI\/entities\//.test(f);
58+
/^(python|nodejs)\/FlightRadarAPI\/entities\//.test(f) ||
59+
// One package per directory in Go: the entities are files.
60+
/^go\/flightradarapi\/(entity|airport|flight)\.go$/.test(f);
4561
4662
const isInPackage = (f) =>
47-
/^(python|nodejs)\/FlightRadarAPI\//.test(f);
63+
/^(python|nodejs)\/FlightRadarAPI\//.test(f) ||
64+
(f.startsWith(goPackage) && !isGoTest(f));
4865
49-
const isTests = (f) => /^(python|nodejs)\/tests\//.test(f);
66+
const isTests = (f) =>
67+
/^(python|nodejs)\/tests\//.test(f) || isGoTest(f);
5068
5169
const isCi = (f) => f.startsWith(".github/");
5270
5371
const isDeps = (f) =>
5472
f === "python/pyproject.toml" ||
5573
f === "nodejs/package.json" ||
56-
f === "nodejs/package-lock.json";
74+
f === "nodejs/package-lock.json" ||
75+
f === "go/go.mod" ||
76+
f === "go/go.sum";
5777
5878
const isBuild = (f) =>
5979
/(^|\/)Makefile$/.test(f) ||
@@ -69,6 +89,7 @@ jobs:
6989
7090
const isPython = (f) => f.startsWith("python/");
7191
const isNode = (f) => f.startsWith("nodejs/");
92+
const isGo = (f) => f.startsWith("go/");
7293
7394
const desired = new Set();
7495
@@ -83,6 +104,7 @@ jobs:
83104
if (isDocs(f)) desired.add("docs");
84105
if (isPython(f)) desired.add("pkg:python");
85106
if (isNode(f)) desired.add("pkg:node");
107+
if (isGo(f)) desired.add("pkg:go");
86108
}
87109
88110
// Dependabot PRs (we only enable Dependabot for security advisories).
@@ -119,7 +141,7 @@ jobs:
119141
"api:core", "api:entities", "api", "tests",
120142
"ci", "build", "feature", "docs",
121143
"revert", "performance", "bug",
122-
"deps", "security", "pkg:python", "pkg:node",
144+
"deps", "security", "pkg:python", "pkg:node", "pkg:go",
123145
]);
124146
125147
const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({

.github/workflows/publish.yml

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,19 @@ jobs:
3232
run: |
3333
py_version=$(grep -oP '__version__\s*=\s*"\K[^"]+' python/FlightRadarAPI/__init__.py)
3434
npm_version=$(node -p "require('./nodejs/package.json').version")
35+
go_version=$(grep -oP 'const Version = "\K[^"]+' go/flightradarapi/doc.go)
3536
echo "python=$py_version" >> "$GITHUB_OUTPUT"
3637
echo "npm=$npm_version" >> "$GITHUB_OUTPUT"
37-
echo "Python: $py_version | npm: $npm_version"
38+
echo "go=$go_version" >> "$GITHUB_OUTPUT"
39+
echo "Python: $py_version | npm: $npm_version | Go: $go_version"
3840
3941
- name: Check versions are in sync
42+
# The Go module is published by the release tag itself, so it has no
43+
# upload job below — only this check keeps its version honest.
4044
run: |
41-
if [ "${{ steps.versions.outputs.python }}" != "${{ steps.versions.outputs.npm }}" ]; then
42-
echo "::error::Version mismatch: python=${{ steps.versions.outputs.python }} npm=${{ steps.versions.outputs.npm }}"
45+
if [ "${{ steps.versions.outputs.python }}" != "${{ steps.versions.outputs.npm }}" ] \
46+
|| [ "${{ steps.versions.outputs.python }}" != "${{ steps.versions.outputs.go }}" ]; then
47+
echo "::error::Version mismatch: python=${{ steps.versions.outputs.python }} npm=${{ steps.versions.outputs.npm }} go=${{ steps.versions.outputs.go }}"
4348
exit 1
4449
fi
4550
@@ -53,6 +58,33 @@ jobs:
5358
exit 1
5459
fi
5560
61+
tag-go-module:
62+
needs: verify-versions
63+
if: github.event_name == 'release'
64+
runs-on: ubuntu-latest
65+
permissions:
66+
contents: write # Required to push the module tag
67+
steps:
68+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
69+
with:
70+
fetch-depth: 0
71+
72+
# A module in a subdirectory is released by a tag carrying that prefix, so
73+
# `go get .../go@latest` sees nothing without this.
74+
- name: Tag the Go module
75+
env:
76+
TAG: ${{ github.event.release.tag_name }}
77+
run: |
78+
version="${TAG#v}"
79+
module_tag="go/v${version}"
80+
if git rev-parse -q --verify "refs/tags/${module_tag}" >/dev/null; then
81+
echo "${module_tag} already exists"
82+
exit 0
83+
fi
84+
git tag "${module_tag}" "${TAG}"
85+
git push origin "${module_tag}"
86+
echo "Pushed ${module_tag}"
87+
5688
publish-pypi:
5789
needs: verify-versions
5890
if: github.event_name == 'release' || inputs.target == 'both' || inputs.target == 'pypi'

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ coverage.xml
2020
# Node.js
2121
node_modules/
2222

23+
# Go
24+
coverage.out
25+
2326
# Version managers (local dev only)
2427
.tool-versions
2528
.python-version

CONTRIBUTING.md

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Contributing to FlightRadarAPI
22

3-
Thanks for your interest. This repo ships two SDKs in parallel — Python and
4-
Node.js — that must stay behavior-aligned, so most non-trivial changes touch
5-
both sides.
3+
Thanks for your interest. This repo ships three SDKs in parallel — Python,
4+
Node.js and Go — that must stay behavior-aligned, so most non-trivial changes
5+
touch every side.
66

77
## Development setup
88

@@ -25,25 +25,37 @@ make lint # eslint
2525
make test-types # tsd
2626
```
2727

28-
## Keeping Python and Node aligned
28+
### Go
29+
```bash
30+
cd go
31+
make deps
32+
make test # offline suite (the PR gate)
33+
make test-integration # live FR24 suite
34+
make lint # gofmt + go vet
35+
make lint-strict # adds staticcheck
36+
```
2937

30-
When you change behavior, change it in both SDKs in the same PR unless there
38+
## Keeping the SDKs aligned
39+
40+
When you change behavior, change it in every SDK in the same PR unless there
3141
is a documented reason not to. Common targets that must stay in sync:
3242

3343
- Error taxonomy (`AirportNotFoundError`, `LoginError`, `CloudflareError`,
3444
`FlightRadarError`).
3545
- `RetryPolicy` semantics (which exceptions are transient, backoff math).
3646
- Cloudflare detection rules.
37-
- The public surface — `FlightRadar24API` methods, the `Countries` enum,
38-
`FlightTrackerConfig` fields, and the `Entity` / `Airport` / `Flight`
39-
attributes consumers depend on.
47+
- The public surface — `FlightRadar24API` methods (`Client` in Go), the
48+
`Countries` enum (`Country` constants in Go), `FlightTrackerConfig` fields,
49+
and the `Entity` / `Airport` / `Flight` attributes consumers depend on.
50+
`docs/go.md` documents where the Go surface deliberately differs.
4051

4152
## Style
4253

4354
- Python: flake8 + mypy.
4455
- Node: eslint + tsd.
56+
- Go: gofmt + go vet + staticcheck.
4557
- Comments must explain **why**, not **what**. The codebase has a few exemplars in
46-
`request.py`/`request.js` — read those before adding new comments.
58+
`request.py`/`request.js`/`request.go` — read those before adding new comments.
4759

4860
## Commits and PRs
4961

@@ -53,10 +65,17 @@ is a documented reason not to. Common targets that must stay in sync:
5365

5466
## Releases
5567

56-
Before publishing a new release, the version **must be bumped**. The version lives in two places:
68+
Before publishing a new release, the version **must be bumped**. The version lives in three places:
5769

5870
- `python/FlightRadarAPI/__init__.py` (`__version__`)
5971
- `nodejs/package.json` (`version`)
72+
- `go/flightradarapi/doc.go` (`Version`)
73+
74+
The Go module needs no registry upload — the tag *is* the release. Because it
75+
lives in a subdirectory, the Go toolchain only sees tags carrying that prefix,
76+
so the `tag-go-module` job of `publish.yml` pushes `go/v1.6.0` alongside the
77+
release tag. Nothing to do by hand; if that job is skipped, `go get
78+
.../go@latest` finds no release and callers fall back to a pseudo-version.
6079

6180
## Reporting bugs and asking questions
6281

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
# FlightRadarAPI
2-
Unofficial SDK for [FlightRadar24](https://www.flightradar24.com/) for Python 3 and Node.js.
2+
Unofficial SDK for [FlightRadar24](https://www.flightradar24.com/) for Python 3, Node.js and Go.
33

44
This SDK should only be used for your own educational purposes. If you are interested in accessing Flightradar24 data commercially, please contact business@fr24.com. See more information at [Flightradar24's terms and conditions](https://www.flightradar24.com/terms-and-conditions).
55

66
**Official FR24 API**: https://fr24api.flightradar24.com/
77

88
[![Python Package](https://github.com/JeanExtreme002/FlightRadarAPI/workflows/Python%20Package/badge.svg)](https://github.com/JeanExtreme002/FlightRadarAPI/actions)
9-
[![Pypi](https://img.shields.io/pypi/v/FlightRadarAPI?logo=pypi)](https://pypi.org/project/FlightRadarAPI/)
9+
[![Node.js Package](https://github.com/JeanExtreme002/FlightRadarAPI/actions/workflows/node-package.yml/badge.svg)](https://github.com/JeanExtreme002/FlightRadarAPI/actions)
10+
[![Go Package](https://github.com/JeanExtreme002/FlightRadarAPI/actions/workflows/go-package.yml/badge.svg)](https://github.com/JeanExtreme002/FlightRadarAPI/actions)
1011
[![License](https://img.shields.io/pypi/l/FlightRadarAPI)](https://github.com/JeanExtreme002/FlightRadarAPI)
12+
[![Pypi](https://img.shields.io/pypi/v/FlightRadarAPI?logo=pypi)](https://pypi.org/project/FlightRadarAPI/)
1113
[![Python Version](https://img.shields.io/badge/python-3.10+-8A2BE2)](https://pypi.org/project/FlightRadarAPI/)
1214
[![Npm](https://img.shields.io/npm/v/flightradarapi?logo=npm&color=red)](https://www.npmjs.com/package/flightradarapi)
15+
[![Node Version](https://img.shields.io/badge/node-18.17+-339933)](https://www.npmjs.com/package/flightradarapi)
16+
[![Go Reference](https://pkg.go.dev/badge/github.com/JeanExtreme002/FlightRadarAPI/go.svg)](https://pkg.go.dev/github.com/JeanExtreme002/FlightRadarAPI/go/flightradarapi)
17+
[![Go Version](https://img.shields.io/badge/go-1.25+-00ADD8)](https://go.dev/dl/)
1318
[![Downloads](https://static.pepy.tech/personalized-badge/flightradarapi?period=total&units=international_system&left_color=grey&right_color=orange&left_text=downloads)](https://pypi.org/project/FlightRadarAPI/)
1419
[![Frequency](https://img.shields.io/pypi/dm/flightradarapi?style=flat&label=frequency)](https://pypi.org/project/FlightRadarAPI/)
1520

@@ -24,8 +29,13 @@ pip install FlightRadarAPI
2429
npm install flightradarapi
2530
```
2631

32+
**For Go:**
33+
```
34+
go get github.com/JeanExtreme002/FlightRadarAPI/go@latest
35+
```
36+
2737
## Documentation
28-
Explore the docs of FlightRadarAPI package, for Python or NodeJS, through [FlightRadarAPI Documentation](https://JeanExtreme002.github.io/FlightRadarAPI/) page.
38+
Explore the docs of FlightRadarAPI package, for Python, NodeJS or Go, through [FlightRadarAPI Documentation](https://JeanExtreme002.github.io/FlightRadarAPI/) page.
2939

3040
## Project resources
3141
**Contributing**: [`CONTRIBUTING.md`](CONTRIBUTING.md)<br>

0 commit comments

Comments
 (0)