Topics covered in this tutorial
- General Background
- Agent Skill
- Libraries
- Forward geocoding
- Reverse geocoding
- Geocoding a list of places
- Running requests in parallel
- Further reading
Background
Before we dive in to the tutorial
- Sign up for an OpenCage geocoding API key.
- Play with the demo page, so that you see the actual response the API returns.
- Browse the API reference, so you understand the optional parameters, best practices, possible response codes, and the rate limiting on free trial accounts.
Agent Skill / Working with AI
Are you developing with AI?
We offer an Agent Skill that includes a reference file specifically about developing code to access our API via PHP.
PHP libraries for accessing the OpenCage Geocoding API
There are two PHP libraries you can use:
The library uses the Guzzle HTTP client to access the API. Both synchronous and asynchronous (parallel) requests are supported.
PHP 8.2 or newer is required. For PHP 8.0 or 8.1 use the 3.x series of the library.
The recommended - and easiest way - to install is via Composer. Require the library in your project's composer.json file.
composer require opencage/geocode
Import the Geocoder class.
require "vendor/autoload.php";
Geocode an address (forward geocoding)
$geocoder = new \OpenCage\Geocoder\Geocoder('YOUR-API-KEY');
# no need to URI encode the query, the library does this for you
$result = $geocoder->geocode('82 Clerkenwell Road, London, UK');
print_r($result);
# set optional parameters
# see the full list: https://opencagedata.com/api#optional-params
#
$result = $geocoder->geocode('6 Rue Massillon, 30020 Nîmes, France', ['language' => 'fr', 'countrycode' => 'fr']);
if ($result && $result['total_results'] > 0) {
$first = $result['results'][0];
print $first['geometry']['lng'] . ';' . $first['geometry']['lat'] . ';' . $first['formatted'] . "\n";
# 4.360081;43.8316276;6 Rue Massillon, 30020 Nîmes, Frankreich
}
Geocode coordinates (reverse geocoding)
$geocoder = new \OpenCage\Geocoder\Geocoder('YOUR-API-KEY');
$result = $geocoder->geocodeReverse(43.831, 4.360); # latitude, longitude
print $result['results'][0]['formatted'];
# 3 Rue de Rivarol, 30020 Nîmes, France
Batch geocode addresses
$geocoder = new \OpenCage\Geocoder\Geocoder('YOUR-API-KEY');
$addresses = ['London', 'Paris, France', 'Berlin'];
$results = [];
foreach ($addresses as $address) {
$result = $geocoder->geocode($address);
$msg = $result['status']['message'];
if ($msg == 'OK'){
$results[$address] = $result;
} else {
error_log("failed to geocode '$address' : $msg");
}
}
Set optional parameters
$result = $geocoder->geocode('6 Rue Massillon, 30020 Nîmes, France', [
'language' => 'fr',
'countrycode' => 'fr'
]);
if ($result && $result['total_results'] > 0) {
$first = $result['results'][0];
print $first['geometry']['lng'] . ';' . $first['geometry']['lat'] . ';' . $first['formatted'] . "\n";
// 4.360081;43.8316276;6 Rue Massillon, 30020 Nîmes, Frankreich
}
Configuration
Set a request timeout (in seconds, the default is 10).
$geocoder->setTimeout(5);
Set a proxy URL. The URL must include a scheme (http, https, or socks5) and a host, and must be set before calling geocode().
$geocoder->setProxy('https://proxy.example.com:1234');
$result = $geocoder->geocode("Brandenburger Tor, Berlin");
print_r($result['results'][0]['formatted']);
// Brandenburger Tor, Unter den Linden, 10117 Berlin, Germany
print_r($result['results'][0]['geometry']);
// Array
// (
// [lat] => 52.5166047
// [lng] => 13.3809897
// )
Geocoding faster by running queries in parallel
The library can run multiple requests concurrently using its asynchronous methods, which is much faster than geocoding a list one at a time.
$geocoder = new \OpenCage\Geocoder\Geocoder('YOUR-API-KEY');
# a single async request returns a promise
$promise = $geocoder->geocodeReverseAsync(51.5074, -0.1278);
$result = $promise->wait();
# run several reverse geocoding requests concurrently
$promises = [
'london' => $geocoder->geocodeReverseAsync(51.5074, -0.1278),
'paris' => $geocoder->geocodeReverseAsync(48.8566, 2.3522),
'tokyo' => $geocoder->geocodeReverseAsync(35.6762, 139.6503),
];
$results = \GuzzleHttp\Promise\Utils::unwrap($promises);
print $results['london']['results'][0]['formatted'];
Free trial keys are limited to one request per second. To stay within
that limit, send the requests in chunks and pause between each chunk.
Raise $chunkSize if your plan allows a higher rate.
$geocoder = new \OpenCage\Geocoder\Geocoder('YOUR-API-KEY');
$addresses = [
'82 Clerkenwell Road, London, EC1M 5RF, UK',
'6 Rue Massillon, 30020 Nîmes, France',
'Brandenburger Tor, 10117 Berlin, Germany',
'Plaza Mayor, 28012 Madrid, Spain',
];
$chunkSize = 1;
$results = [];
foreach (array_chunk($addresses, $chunkSize) as $chunk) {
$promises = [];
foreach ($chunk as $address) {
$promises[$address] = $geocoder->geocodeAsync($address);
}
# wait for this chunk to finish before starting the next one
$results += \GuzzleHttp\Promise\Utils::unwrap($promises);
sleep(1); # pause before the next chunk
}
foreach ($results as $address => $result) {
print $address . ' => ' . $result['results'][0]['formatted'] . "\n";
}
We also created an example how to geocode a file with parallel threads.
Before you start geocoding at high volume, please read our guide to geocoding large datasets where we explain various strategies and points to consider.The recommended - and easiest way - to install is via Composer. Require the library in your project's composer.json file.
composer require willdurand/geocoder
Import the Geocoder class.
require "vendor/autoload.php";
use Http\Adapter\Guzzle6\Client as GuzzleAdapter;
use Geocoder\Query\GeocodeQuery;
use Geocoder\Query\ReverseQuery;
Geocode an address (forward geocoding)
$adapter = new GuzzleAdapter();
$provider = new \Geocoder\Provider\OpenCage\OpenCage($adapter, 'YOUR-API-KEY');
$geocoder = new \Geocoder\StatefulGeocoder($provider, 'en');
$results = $geocoder->geocodeQuery(GeocodeQuery::create('1 Hacker Way, Menlo Park, 94025'));
# print_r($results);
$coords = $results->first()->getCoordinates();
echo json_encode([ 'lat' => $coords->getLatitude(), 'lon' => $coords->getLongitude() ]) . "\n";
Batch geocode addresses
$adapter = new GuzzleAdapter();
$provider = new \Geocoder\Provider\OpenCage\OpenCage($adapter, 'YOUR-API-KEY');
$geocoder = new \Geocoder\StatefulGeocoder($provider, 'en');
$addresses = ['London', 'Paris', 'Berlin'];
$results = [];
foreach ($addresses as $address) {
$result = $geocoder->geocodeQuery(GeocodeQuery::create($address));
$msg = $result['status']['message'];
if ($msg === 'OK') {
$results[$address] = $result;
} else {
error_log("failed to geocode '$addresses' : $msg");
}
}
Geocode coordinates (reverse geocoding)
$adapter = new GuzzleAdapter();
$provider = new \Geocoder\Provider\OpenCage\OpenCage($adapter, 'YOUR-API-KEY');
$geocoder = new \Geocoder\StatefulGeocoder($provider, 'en');
$results = $geocoder->reverseQuery(ReverseQuery::fromCoordinates(37.4856225, -122.1468803));
# print_r($results);
echo $results->first()->getStreetName() . "\n";
2,500 geocoding API requests/day - No credit card required