IPGeolocation Swift SDK
Overview
Official Swift SDK for the IPGeolocation.io IP Location API.
Look up IPv4, IPv6, and domains with /v3/ipgeo and /v3/ipgeo-bulk . Get geolocation, company, ASN, timezone, network, hostname, abuse, user-agent, currency, and security data from one API.
- Swift Package targeting Swift 5.9, macOS 12+, iOS 15+, tvOS 15+, and watchOS 8+
- Typed JSON responses plus raw JSON and XML methods
- Built on
URLSessionwith no third-party dependencies
Install
Add the package to your Swift Package Manager dependencies.
Swift Package Manifest:
.package(
url: "https://github.com/IPGeolocation/ip-geolocation-api-swift-sdk.git",
from: "1.0.0"
)Then add IPGeolocation to your target's dependencies:
.target(
name: "YourTarget",
dependencies: [
.product(name: "IPGeolocation", package: "ip-geolocation-api-swift-sdk"),
]
)Xcode: in File → Add Package Dependencies…, enter the repository URL and pick the latest 1.x release.
Package page: https://github.com/IPGeolocation/ip-geolocation-api-swift-sdk
All public types live in the IPGeolocation module. Xcode will import the types you use in the examples below automatically.
Quick Start
import Foundation
import IPGeolocation
struct MissingAPIKey: Error {}
func lookupExample() async throws {
guard let apiKey = ProcessInfo.processInfo.environment["IPGEO_API_KEY"] else {
throw MissingAPIKey()
}
let client = IPGeolocationClient(
config: try IPGeolocationClientConfig(apiKey: apiKey)
)
defer { client.close() }
let response = try await client.lookupIPGeolocation(
LookupIPGeolocationRequest(ip: "8.8.8.8")
)
print(response.data.ip ?? "-") // 8.8.8.8
print(response.data.location?.countryName ?? "-") // United States
print(response.data.location?.city ?? "-")
print(response.data.timeZone?.name ?? "-")
print(response.metadata.creditsCharged ?? 0)
}You can also call lookupIPGeolocation() with no request object to resolve the caller IP.
At a Glance
| Item | Value |
|---|---|
| Product | Swift Package |
| Module | IPGeolocation |
| Supported Endpoints | /v3/ipgeo , /v3/ipgeo-bulk |
| Supported Inputs | IPv4, IPv6, domain |
| Main Data Returned | Geolocation, company, ASN, timezone, network, hostname, abuse, user-agent, currency, security |
| Authentication | API key, request-origin auth for /v3/ipgeo only |
| Response Formats | Typed JSON, raw JSON, raw XML |
| Bulk Limit | Up to 50,000 IPs or domains per request |
| Transport | Foundation URLSession |
Get Your API Key
Create an IPGeolocation account and copy an API key from your dashboard.
- Sign up: https://app.ipgeolocation.io/signup
- Verify your email if prompted
- Sign in: https://app.ipgeolocation.io/login
- Open your dashboard: https://app.ipgeolocation.io/dashboard
- Copy an API key from the
API Keyssection
For server-side Swift code, keep the API key in an environment variable or a secret manager. For browser-based single lookups on paid plans, use request-origin auth instead of exposing an API key in frontend code.
Authentication
1. API Key
import Foundation
import IPGeolocation
let apiKey = ProcessInfo.processInfo.environment["IPGEO_API_KEY"] ?? ""
let client = IPGeolocationClient(
config: try IPGeolocationClientConfig(apiKey: apiKey)
)If IPGEO_API_KEY is missing, IPGeolocationClientConfig throws ValidationError("apiKey must not be blank") so the failure stays typed instead of crashing the host app.
2. Request-Origin Auth
let client = IPGeolocationClient(
config: try IPGeolocationClientConfig(
requestOrigin: "https://app.example.com"
)
) requestOrigin must be an absolute http or https origin with no path, query string, fragment, or userinfo.
Plan Behavior
Feature availability depends on your plan and request parameters.
| Capability | Free | Paid |
|---|---|---|
| Single IPv4 and IPv6 lookup | Supported | Supported |
| Domain lookup | Not supported | Supported |
| Bulk lookup | Not supported | Supported |
Non-English lang | Not supported | Supported |
| Request-origin auth | Not supported | Supported for /v3/ipgeo only |
Optional modules via include | Not supported | Supported |
include: ["*"] | Base response only | All plan-available modules |
Paid plans still need include for optional modules. fields and excludes only trim the response. They do not turn modules on or unlock paid data.
Client Configuration
| Field | Type | Default | Notes |
|---|---|---|---|
apiKey | String? | unset | Required for bulk lookup. Optional for single lookup if requestOrigin is set. |
requestOrigin | String? | unset | Must be an absolute http or https origin with no path, query string, fragment, or userinfo. |
baseURL | String | https://api.ipgeolocation.io | Override the API base URL. |
requestTimeout | TimeInterval | 10 | Maximum time URLSession will wait for new data on an in-flight request (connect, headers, or inter-chunk body inactivity). Must be greater than zero and less than or equal to resourceTimeout . Maps to URLSessionConfiguration.timeoutIntervalForRequest . |
resourceTimeout | TimeInterval | 30 | Maximum total time for the entire request. Must be greater than zero. Maps to URLSessionConfiguration.timeoutIntervalForResource . |
maxResponseBodyBytes | Int | 33554432 | Maximum response body size. The SDK enforces this while streaming the body and throws TransportError as soon as the limit is exceeded. Must be greater than zero. |
allowInsecureHTTP | Bool | Off by default. The SDK sends apiKey in the query string, so http:// would transmit it in plaintext. Set to true only for local testing or a trusted proxy where you have accepted the risk. |
Config values are validated when the config is built. The initializer throws ValidationError on invalid input. Request values are validated before each request is sent.
URLSession does not expose a connect-only timer, so the SDK uses URLSession 's native two-knob model: requestTimeout is the inactivity timer that also fires on a stalled body read, and resourceTimeout caps the total time. This is honest about the behavior you actually get on iOS and macOS.
Available Methods
| Method | Returns | Notes |
|---|---|---|
lookupIPGeolocation(_:) | APIResponse<IPGeolocationResponse> | Single lookup. Typed JSON response. |
lookupIPGeolocationRaw(_:) | APIResponse<String> | Single lookup. Raw JSON or XML string. |
bulkLookupIPGeolocation(_:) | APIResponse<[BulkLookupResult]> | Bulk lookup. Typed JSON response. |
bulkLookupIPGeolocationRaw(_:) | APIResponse<String> | Bulk lookup. Raw JSON or XML string. |
close() | Void | Idempotent. Finishes in-flight tasks and invalidates the session. |
IPGeolocationClient.defaultUserAgent() | String | Returns the SDK default outbound User-Agent header value. |
Request Options
| Field | Applies To | Notes |
|---|---|---|
ip | Single lookup | IPv4, IPv6, or domain. Leave it nil for caller IP lookup. |
ips | Bulk lookup | Array of 1 to 50,000 IPs or domains. |
lang | Single and bulk | Language.en , .de , .ru , .ja , .fr , .cn , .es , .cs , .it , .ko , .fa , .pt . |
include | Single and bulk | Array of module names such as security , abuse , user_agent , hostname , liveHostname , hostnameFallbackLive , geo_accuracy , dma_code , or * . |
fields | Single and bulk | Array of field paths to keep, for example location.country_name or security.threat_score . |
excludes | Single and bulk | Array of field paths to remove from the response. |
userAgent | Single and bulk | Overrides the outbound User-Agent header. |
headers | Single and bulk | Extra request headers. Header names are normalized to canonical case. |
output | Single and bulk | .json or .xml . Typed methods require .json . |
JSON Keys and Swift Names
The API returns JSON keys such as country_metadata and calling_code .
In Swift code, the SDK uses countryMetadata and callingCode .
The data is the same. Only the names you use in Swift change.
| API JSON key | Swift name |
|---|---|
country_metadata | response.data.countryMetadata |
calling_code | response.data.countryMetadata?.callingCode |
time_zone | response.data.timeZone |
current_tz_abbreviation | response.data.timeZone?.currentTZAbbreviation |
is_eu | response.data.location?.isEU |
user_agent | response.data.userAgent |
version_major | response.data.userAgent?.versionMajor |
Example:
1{
2 "country_metadata": {
3 "calling_code": "+46"
4 },
5 "time_zone": {
6 "current_tz_abbreviation": "CET"
7 },
8 "location": {
9 "is_eu": true
10 }
11}print(response.data.countryMetadata?.callingCode ?? "-")
print(response.data.timeZone?.currentTZAbbreviation ?? "-")
print(response.data.location?.isEU ?? false)There are two different places where names show up:
- In Swift code, use the names shown by the SDK, such as
response.data.countryMetadata?.callingCode - In request parameters, use the API names from the documentation
include example:
let response = try await client.lookupIPGeolocation(
LookupIPGeolocationRequest(
ip: "8.8.8.8",
include: ["user_agent"]
)
)
print(response.data.userAgent?.name ?? "-") fields example:
let response = try await client.lookupIPGeolocation(
LookupIPGeolocationRequest(
ip: "8.8.8.8",
fields: ["country_metadata.calling_code"]
)
)
print(response.data.countryMetadata?.callingCode ?? "-") excludes example:
let response = try await client.lookupIPGeolocation(
LookupIPGeolocationRequest(
ip: "8.8.8.8",
excludes: ["time_zone.current_tz_abbreviation"]
)
)
print(response.data.timeZone?.currentTZAbbreviation ?? "-")More request-parameter examples:
-
include: ["security", "abuse"] -
include: ["hostnameFallbackLive"] -
fields: ["country_metadata.calling_code"] -
fields: ["time_zone.current_tz_abbreviation"] -
excludes: ["location.is_eu"]
Examples
The examples below assume you already have a configured client in scope:
import Foundation
import IPGeolocation
let apiKey = ProcessInfo.processInfo.environment["IPGEO_API_KEY"] ?? ""
let client = IPGeolocationClient(
config: try IPGeolocationClientConfig(apiKey: apiKey)
)1. Caller IP
Leave ip empty to look up the public IP of the machine making the request.
let response = try await client.lookupIPGeolocation()
print(response.data.ip ?? "-")2. Domain Lookup
Domain lookup is a paid-plan feature.
let response = try await client.lookupIPGeolocation(
LookupIPGeolocationRequest(ip: "ipgeolocation.io")
)
print(response.data.domain ?? "-") // ipgeolocation.io
print(response.data.location?.countryName ?? "-")3. Security and Abuse
let response = try await client.lookupIPGeolocation(
LookupIPGeolocationRequest(
ip: "9.9.9.9",
include: ["security", "abuse"]
)
)
print(response.data.security?.threatScore ?? 0)
print(response.data.abuse?.emails?.first ?? "-")4. User-Agent Parsing
To parse a visitor user-agent string, pass include: ["user_agent"] and set the userAgent field on the request. The SDK uses it as the outbound User-Agent header, and the API parses it and returns the result in response.data.userAgent .
let visitorUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9"
let response = try await client.lookupIPGeolocation(
LookupIPGeolocationRequest(
ip: "115.240.90.163",
include: ["user_agent"],
userAgent: visitorUA
)
)
print(response.data.userAgent?.name ?? "-")
print(response.data.userAgent?.operatingSystem?.name ?? "-")5. Filtered Response
let response = try await client.lookupIPGeolocation(
LookupIPGeolocationRequest(
ip: "8.8.8.8",
include: ["security"],
fields: [
"location.country_name",
"security.threat_score",
"security.is_vpn",
],
excludes: ["currency"]
)
)
print(response.data.location?.countryName ?? "-")
print(response.data.security?.isVPN ?? false)
print(response.data.currency == nil)6. Raw XML
let response = try await client.lookupIPGeolocationRaw(
LookupIPGeolocationRequest(ip: "8.8.8.8", output: .xml)
)
print(response.data)7. Bulk Lookup
let response = try await client.bulkLookupIPGeolocation(
BulkLookupIPGeolocationRequest(ips: ["8.8.8.8", "1.1.1.1"])
)
for result in response.data {
if result.isSuccess {
print(result.data?.ip ?? "-")
} else {
print(result.error?.message ?? "-")
}
}8. Raw Bulk JSON
let response = try await client.bulkLookupIPGeolocationRaw(
BulkLookupIPGeolocationRequest(ips: ["8.8.8.8", "1.1.1.1"])
)
print(response.data)Response Metadata
Every method returns APIResponse<T> , where:
-
datacontains the typed object or raw response string -
metadatacontains response details such as:-
creditsCharged -
successfulRecords -
statusCode -
durationMs -
rawHeaders
-
Example:
print(response.metadata.statusCode)
print(response.metadata.durationMs)
print(response.metadata.firstHeaderValue("content-type") ?? "-")JSON Helpers
Use JSONOutput for logs, debugging, or CLI output.
| Method | Notes |
|---|---|
JSONOutput.toJSON(value) | Compact JSON. Omits nil fields. |
JSONOutput.toJSON(value, mode: .full) | Includes nil fields as null . |
JSONOutput.toPrettyJSON(value) | Pretty-printed compact JSON. |
JSONOutput.toPrettyJSON(value, mode: .full) | Pretty-printed full JSON. |
Example:
let compact = try JSONOutput.toPrettyJSON(response.data)
let full = try JSONOutput.toPrettyJSON(response.data, mode: .full)
print(compact)
print(full)Errors
All SDK errors conform to the IPGeolocationError protocol.
| Error Type | When it happens |
|---|---|
ValidationError | Invalid config, invalid request values, or typed XML request |
RequestTimeoutError | Request or resource timeout |
TransportError | Network or transport failure |
SerializationError | Request or response serialization failure |
APIError | API returned a non-2xx response |
APIError exposes:
-
statusCode -
message -
apiMessage -
kind(APIErrorKind, which covers.badRequest,.unauthorized,.forbidden,.notFound,.methodNotAllowed,.contentTooLarge,.unsupportedMediaType,.locked,.tooManyRequests,.clientClosedRequest,.internalServerError,.other)
Example:
do {
_ = try await client.lookupIPGeolocation(
LookupIPGeolocationRequest(ip: "8.8.8.8", output: .xml)
)
} catch let error as ValidationError {
print(error.message)
}Troubleshooting
- Bulk lookup always requires
apiKey.requestOriginis not enough. - Typed methods only support JSON. Use the raw methods for XML.
- If you need security, abuse, user-agent, or hostname data, include those modules explicitly.
-
fieldsandexcludesfilter the response. They do not unlock paid data. -
requestOriginmust be an origin only. Do not include a path, query string, fragment, or userinfo. - Response fields are optional so omitted fields stay omitted.
-
requestTimeoutmust be less than or equal toresourceTimeout. - The SDK rejects responses larger than
maxResponseBodyBytes. The default cap is32 MiB.
Frequently Asked Questions
lookupIPGeolocationRaw or bulkLookupIPGeolocationRaw for XML.async throws . The SDK targets platforms that support Swift Concurrency (macOS 12+, iOS 15+, tvOS 15+, watchOS 8+).Links
- Homepage: https://ipgeolocation.io
- IP Location API product page: https://ipgeolocation.io/ip-location-api.html
- Documentation: https://ipgeolocation.io/documentation/ip-location-api.html
- GitHub repository: https://github.com/IPGeolocation/ip-geolocation-api-swift-sdk