├── go.mod ├── .gitignore ├── go.sum ├── LICENSE ├── ipapi_test.go ├── ipapi.go └── README.md /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ipqwery/ipapi-go 2 | 3 | go 1.24.2 4 | 5 | require ( 6 | github.com/goccy/go-json v0.10.5 7 | github.com/valyala/fasthttp v1.62.0 8 | ) 9 | 10 | require ( 11 | github.com/andybalholm/brotli v1.1.1 // indirect 12 | github.com/klauspost/compress v1.18.0 // indirect 13 | github.com/valyala/bytebufferpool v1.0.0 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | go.work.sum 23 | 24 | # env file 25 | .env 26 | 27 | # IDE files 28 | .idea/ 29 | .vscode/ 30 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= 2 | github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= 3 | github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= 4 | github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 5 | github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= 6 | github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 7 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 8 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 9 | github.com/valyala/fasthttp v1.62.0 h1:8dKRBX/y2rCzyc6903Zu1+3qN0H/d2MsxPPmVNamiH0= 10 | github.com/valyala/fasthttp v1.62.0/go.mod h1:FCINgr4GKdKqV8Q0xv8b+UxPV+H/O5nNFo3D+r54Htg= 11 | github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= 12 | github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 IPQuery 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ipapi_test.go: -------------------------------------------------------------------------------- 1 | package ipapi 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | ) 8 | 9 | const mockIP = "1.1.1.1" 10 | 11 | func TestQueryIP(t *testing.T) { 12 | ipInfo, err := QueryIP("8.8.8.8") 13 | if err != nil { 14 | t.Fatalf("Failed to query IP: %v", err) 15 | } 16 | if ipInfo.IP != "8.8.8.8" { 17 | t.Errorf("Expected IP to be '8.8.8.8', got %s", ipInfo.IP) 18 | } 19 | } 20 | 21 | func TestQueryOwnIP(t *testing.T) { 22 | ip, err := QueryOwnIP() 23 | if err != nil { 24 | t.Fatalf("Failed to fetch own IP: %v", err) 25 | } 26 | if ip == "" { 27 | t.Error("Expected non-empty IP") 28 | } 29 | } 30 | 31 | // go test -bench=BenchmarkQueryIP -benchmem 32 | func BenchmarkQueryIP(b *testing.B) { 33 | for i := 0; i < b.N; i++ { 34 | _, err := QueryIP(mockIP) 35 | if err != nil { 36 | b.Fatalf("QueryIP failed: %v", err) 37 | } 38 | } 39 | } 40 | 41 | func mockServer(response string, statusCode int) *httptest.Server { 42 | return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 43 | w.WriteHeader(statusCode) 44 | w.Write([]byte(response)) 45 | })) 46 | } 47 | 48 | // go test -bench=BenchmarkQueryOwnIP -benchmem 49 | func BenchmarkQueryOwnIP(b *testing.B) { 50 | mockResp := `{"ip":"127.0.0.1"}` 51 | server := mockServer(mockResp, http.StatusOK) 52 | defer server.Close() 53 | 54 | baseURL = server.URL 55 | 56 | for i := 0; i < b.N; i++ { 57 | _, err := QueryOwnIP() 58 | if err != nil { 59 | b.Fatalf("QueryOwnIP failed: %v", err) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ipapi.go: -------------------------------------------------------------------------------- 1 | package ipapi 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/goccy/go-json" 7 | "github.com/valyala/fasthttp" 8 | ) 9 | 10 | var baseURL = "https://api.ipquery.io/" 11 | 12 | // ISPInfo represents information about the ISP of an IP address. 13 | type ISPInfo struct { 14 | ASN string `json:"asn,omitempty"` 15 | Org string `json:"org,omitempty"` 16 | ISP string `json:"isp,omitempty"` 17 | } 18 | 19 | // LocationInfo represents geographical information about an IP address. 20 | type LocationInfo struct { 21 | Country string `json:"country,omitempty"` 22 | CountryCode string `json:"country_code,omitempty"` 23 | City string `json:"city,omitempty"` 24 | State string `json:"state,omitempty"` 25 | ZipCode string `json:"zipcode,omitempty"` 26 | Latitude float64 `json:"latitude,omitempty"` 27 | Longitude float64 `json:"longitude,omitempty"` 28 | Timezone string `json:"timezone,omitempty"` 29 | Localtime string `json:"localtime,omitempty"` 30 | } 31 | 32 | // RiskInfo represents risk information about an IP address. 33 | type RiskInfo struct { 34 | IsMobile bool `json:"is_mobile,omitempty"` 35 | IsVPN bool `json:"is_vpn,omitempty"` 36 | IsTor bool `json:"is_tor,omitempty"` 37 | IsProxy bool `json:"is_proxy,omitempty"` 38 | IsDatacenter bool `json:"is_datacenter,omitempty"` 39 | RiskScore int `json:"risk_score,omitempty"` 40 | } 41 | 42 | // IPInfo represents all the information returned by the API. 43 | type IPInfo struct { 44 | IP string `json:"ip"` 45 | ISP *ISPInfo `json:"isp,omitempty"` 46 | Location *LocationInfo `json:"location,omitempty"` 47 | Risk *RiskInfo `json:"risk,omitempty"` 48 | } 49 | 50 | // QueryIP fetches information for a specific IP address. 51 | func QueryIP(ip string) (*IPInfo, error) { 52 | statusCode, body, err := fasthttp.Get(nil, baseURL+ip) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | if statusCode != fasthttp.StatusOK { 58 | return nil, fmt.Errorf("failed to fetch IP info: status code %d", statusCode) 59 | } 60 | 61 | var ipInfo IPInfo 62 | if err = json.Unmarshal(body, &ipInfo); err != nil { 63 | return nil, err 64 | } 65 | 66 | return &ipInfo, nil 67 | } 68 | 69 | // QueryOwnIP fetches information about the current machine's public IP. 70 | func QueryOwnIP() (string, error) { 71 | statusCode, body, err := fasthttp.Get(nil, baseURL) 72 | if err != nil { 73 | return "", err 74 | } 75 | 76 | if statusCode != fasthttp.StatusOK { 77 | return "", fmt.Errorf("failed to fetch own IP: status code %d", statusCode) 78 | } 79 | 80 | return string(body), nil 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ipapi-go 2 | 3 | A Go package to query IP addresses using the [ipquery.io](https://ipquery.io) API. 4 | 5 | ## Overview 6 | 7 | `ipapi-go` is a lightweight Go package that allows you to fetch detailed information about IP addresses, including ISP details, location data, and risk assessment, using the `ipquery.io` API. You can use this package to query individual IP addresses, perform bulk lookups, and even find out your own public IP address. 8 | 9 | ## Features 10 | 11 | - Query detailed information for a specific IP address. 12 | - Fetch geographical and ISP data. 13 | - Check if an IP address is associated with VPNs, proxies, or data centers. 14 | - Get the public IP address of your own machine. 15 | 16 | ## Installation 17 | 18 | To install the package, run: 19 | 20 | ```bash 21 | go get github.com/ipqwery/ipapi-go 22 | ``` 23 | 24 | ## Importing the Package 25 | 26 | ```go 27 | import "github.com/ipqwery/ipapi-go" 28 | ``` 29 | 30 | ## Usage Examples 31 | 32 | ### Query a Specific IP Address 33 | 34 | You can fetch detailed information about a specific IP address using the `QueryIP` function: 35 | 36 | ```go 37 | package main 38 | 39 | import ( 40 | "fmt" 41 | "github.com/ipqwery/ipapi-go" 42 | ) 43 | 44 | func main() { 45 | ipInfo, err := ipapi.QueryIP("8.8.8.8") 46 | if err != nil { 47 | fmt.Println("Error querying IP:", err) 48 | return 49 | } 50 | fmt.Printf("IP Info: %+v\n", ipInfo) 51 | } 52 | ``` 53 | 54 | #### Example Output 55 | ``` 56 | IP Info: &{IP:8.8.8.8 ISP:{ASN:AS15169 Org:Google LLC ISP:Google LLC} Location:{Country:United States CountryCode:US City:Mountain View State:California ZipCode:94035 Latitude:37.386 Longitude:-122.0838 Timezone:America/Los_Angeles Localtime:2024-11-09T12:45:32} Risk:{IsMobile:false IsVPN:false IsTor:false IsProxy:false IsDatacenter:true RiskScore:0}} 57 | ``` 58 | 59 | ### Fetch Your Own Public IP Address 60 | 61 | To fetch the public IP address of the machine running your code, use the `QueryOwnIP` function: 62 | 63 | ```go 64 | package main 65 | 66 | import ( 67 | "fmt" 68 | "github.com/ipqwery/ipapi-go" 69 | ) 70 | 71 | func main() { 72 | ip, err := ipapi.QueryOwnIP() 73 | if err != nil { 74 | fmt.Println("Error fetching own IP:", err) 75 | return 76 | } 77 | fmt.Printf("Your IP: %s\n", ip) 78 | } 79 | ``` 80 | 81 | #### Example Output 82 | ``` 83 | Your IP: 203.0.113.45 84 | ``` 85 | 86 | ### Struct Definitions 87 | 88 | Below are the definitions of the key data structures used in this package: 89 | 90 | - **ISPInfo**: Contains information about the Internet Service Provider (ISP). 91 | - **LocationInfo**: Holds geographical data such as country, city, and coordinates. 92 | - **RiskInfo**: Provides information on potential risks associated with the IP (e.g., if it's a VPN or proxy). 93 | - **IPInfo**: The main structure that combines all the information. 94 | 95 | #### Example: 96 | ```go 97 | type ISPInfo struct { 98 | ASN string `json:"asn,omitempty"` 99 | Org string `json:"org,omitempty"` 100 | ISP string `json:"isp,omitempty"` 101 | } 102 | 103 | type LocationInfo struct { 104 | Country string `json:"country,omitempty"` 105 | CountryCode string `json:"country_code,omitempty"` 106 | City string `json:"city,omitempty"` 107 | State string `json:"state,omitempty"` 108 | ZipCode string `json:"zipcode,omitempty"` 109 | Latitude float64 `json:"latitude,omitempty"` 110 | Longitude float64 `json:"longitude,omitempty"` 111 | Timezone string `json:"timezone,omitempty"` 112 | Localtime string `json:"localtime,omitempty"` 113 | } 114 | 115 | type RiskInfo struct { 116 | IsMobile bool `json:"is_mobile,omitempty"` 117 | IsVPN bool `json:"is_vpn,omitempty"` 118 | IsTor bool `json:"is_tor,omitempty"` 119 | IsProxy bool `json:"is_proxy,omitempty"` 120 | IsDatacenter bool `json:"is_datacenter,omitempty"` 121 | RiskScore int `json:"risk_score,omitempty"` 122 | } 123 | 124 | type IPInfo struct { 125 | IP string `json:"ip"` 126 | ISP *ISPInfo `json:"isp,omitempty"` 127 | Location *LocationInfo `json:"location,omitempty"` 128 | Risk *RiskInfo `json:"risk,omitempty"` 129 | } 130 | ``` 131 | 132 | ## Testing 133 | 134 | The package includes unit tests to ensure functionality. To run the tests: 135 | 136 | ```bash 137 | go test -v 138 | ``` 139 | 140 | ### Example Tests 141 | 142 | Here's an example of how the tests are structured: 143 | 144 | ```go 145 | package ipapi 146 | 147 | import "testing" 148 | 149 | func TestQueryIP(t *testing.T) { 150 | ipInfo, err := QueryIP("8.8.8.8") 151 | if err != nil { 152 | t.Fatalf("Failed to query IP: %v", err) 153 | } 154 | if ipInfo.IP != "8.8.8.8" { 155 | t.Errorf("Expected IP to be '8.8.8.8', got %s", ipInfo.IP) 156 | } 157 | } 158 | 159 | func TestQueryOwnIP(t *testing.T) { 160 | ip, err := QueryOwnIP() 161 | if err != nil { 162 | t.Fatalf("Failed to fetch own IP: %v", err) 163 | } 164 | if ip == "" { 165 | t.Error("Expected non-empty IP") 166 | } 167 | } 168 | ``` 169 | 170 | ## License 171 | 172 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 173 | 174 | ## Acknowledgements 175 | 176 | - This package uses the [ipquery.io](https://ipquery.io) API for IP information. 177 | - Inspired by various IP geolocation services. 178 | 179 | ## Links 180 | 181 | - [GitHub Repository](https://github.com/ipqwery/ipapi-go) 182 | - [pkg.go.dev Documentation](https://pkg.go.dev/github.com/ipqwery/ipapi-go) 183 | --------------------------------------------------------------------------------