People Groups APi
Base URL: https://peoplegroups.org/wp-json/pg/v1
All endpoints are read-only and publicly accessible. No authentication is required.
Endpoints
List People Groups
GET /people-groups
Returns a paginated list of all people group records.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 | Page number (1-based). |
per_page |
integer | 100 | Results per page. Maximum 250. |
Response Headers
| Header | Description |
|---|---|
X-WP-Total |
Total number of people group records in the dataset. |
X-WP-TotalPages |
Total number of pages at the requested per_page size. |
Example Request
GET https://peoplegroups.org/wp-json/pg/v1/people-groups?page=1&per_page=100
Example Response
[
{
"PGID": "PG012345",
"NmDisp": "Nateni",
"Ctry": "Benin",
"Pop": 131000,
"Rlgn": "Ethnoreligion - Animism",
"Lang": "Nateni",
"LPI": 1,
"LPIname": "Pioneer Unreached People Group",
"SPI": 1,
"SPIdesc": "Engaged yet Unreached",
"GSEC": 1,
"GSECbrf": "Less than 2% Evangelical, No Active CP Activity",
"Latitude": 10.52,
"Longitude": 1.22,
...
},
...
]
Get a Single People Group
GET /people-groups/{pgid}
Returns a single people group record by PGID.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
pgid |
string | People Group ID — e.g. PG012345. |
Example Request
GET https://peoplegroups.org/wp-json/pg/v1/people-groups/PG012345
Example Response
{
"OBJECTID": 10508,
"PEID": 12345,
"PGID": "PG012345",
"Name": "Nateni",
"NmDisp": "Nateni",
"NmAlt": null,
"ISOalpha3": "BEN",
"Ctry": "Benin",
"Regn": "Africa",
"RegnSub": "Western Africa",
"Pop": 131000,
"Rlgn": "Ethnoreligion - Animism",
"Lang": "Nateni",
"LangFamily": "Atlantic-Congo",
"ROL": "ntm",
"LPI": 1,
"LPIname": "Pioneer Unreached People Group",
"LPIdesc": "0.1% to 0.5% Evangelical",
"SPI": 1,
"SPIdesc": "Engaged yet Unreached",
"GSEC": 1,
"GSECbrf": "Less than 2% Evangelical, No Active CP Activity",
"GSEClng": "this people group is less than 2% evangelical, some evangelical resources are available, but there has been no active church planting among them within the past two years",
"EvngLvl": "Less than 2%",
"CongExst": "Yes",
"Plnting": "No Churches Planted",
"EngStat": "Engaged",
"Bible": "Available",
"Jesus": "Not Available",
"ResTot": 3,
"PeopleDesc": "an indigenous community of Benin; a dialect subgroup of Nateni (ntm)",
"PicURL": "https://joshuaproject.net/assets/media/profiles/photos/p13251.jpg",
"PicCrdt": "Photo courtesy of Joshua Project. Photo Source: Matt & Sarah Murdock",
"Photo": "Y",
"Latitude": 10.52,
"Longitude": 1.22,
"UpdatedDate": "2026-03-27T04:24:27.000+00:00"
}
Error Response — 404 Not Found
{
"code": "pg_not_found",
"message": "No people group found for PGID: PG999999",
"data": { "status": 404 }
}
Code Examples – Simple
Requires the requests library (pip install requests).
Fetch a single people group
import requests
response = requests.get(
"https://peoplegroups.org/wp-json/pg/v1/people-groups/PG012345"
)
response.raise_for_status()
group = response.json()
print(group["NmDisp"]) # Nateni
print(group["Ctry"]) # Benin
print(group["LPIname"]) # Pioneer Unreached People Group
Page through all people groups
import requests
BASE_URL = "https://peoplegroups.org/wp-json/pg/v1/people-groups"
page = 1
all_groups = []
while True:
response = requests.get(BASE_URL, params={"page": page, "per_page": 250})
response.raise_for_status()
batch = response.json()
all_groups.extend(batch)
total_pages = int(response.headers.get("X-WP-TotalPages", 1))
if page >= total_pages:
break
page += 1
print(f"Fetched {len(all_groups)} people groups")
Uses the native fetch API (Node 18+). No dependencies required.
Fetch a single people group
const response = await fetch(
"https://peoplegroups.org/wp-json/pg/v1/people-groups/PG012345"
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
const group = await response.json();
console.log(group.NmDisp); // Nateni
console.log(group.Ctry); // Benin
console.log(group.LPIname); // Pioneer Unreached People Group
Page through all people groups
const BASE_URL = "https://peoplegroups.org/wp-json/pg/v1/people-groups";
let page = 1;
let totalPages = 1;
const allGroups = [];
do {
const response = await fetch(`${BASE_URL}?page=${page}&per_page=250`);
const batch = await response.json();
allGroups.push(...batch);
totalPages = parseInt(response.headers.get("X-WP-TotalPages") ?? "1", 10);
page++;
} while (page <= totalPages);
console.log(`Fetched ${allGroups.length} people groups`);
Fetch a single people group (WordPress context)
$response = wp_remote_get(
'https://peoplegroups.org/wp-json/pg/v1/people-groups/PG012345'
);
if ( is_wp_error( $response ) ) {
error_log( $response->get_error_message() );
return;
}
$group = json_decode( wp_remote_retrieve_body( $response ), true );
echo esc_html( $group['NmDisp'] ); // Nateni
echo esc_html( $group['Ctry'] ); // Benin
Fetch a single people group (plain PHP)
$pgid = 'PG012345';
$url = 'https://peoplegroups.org/wp-json/pg/v1/people-groups/' . urlencode( $pgid );
$json = file_get_contents( $url );
if ( $json === false ) {
throw new RuntimeException( 'Failed to fetch people group data' );
}
$group = json_decode( $json, true );
echo htmlspecialchars( $group['NmDisp'] ); // Nateni
Page through all people groups
$base_url = 'https://peoplegroups.org/wp-json/pg/v1/people-groups';
$page = 1;
$total_pages = 1;
$all_groups = [];
do {
$url = $base_url . '?' . http_build_query( [ 'page' => $page, 'per_page' => 250 ] );
$response = wp_remote_get( $url );
if ( is_wp_error( $response ) ) {
break;
}
$batch = json_decode( wp_remote_retrieve_body( $response ), true );
$all_groups = array_merge( $all_groups, $batch );
$total_pages = (int) wp_remote_retrieve_header( $response, 'x-wp-totalpages' );
$page++;
} while ( $page <= $total_pages );
echo count( $all_groups ) . ' people groups fetched';
Requires reqwest and serde_json. Add to Cargo.toml:
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
Fetch a single people group
use reqwest;
use serde_json::Value;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let url = "https://peoplegroups.org/wp-json/pg/v1/people-groups/PG012345";
let group: Value = reqwest::get(url).await?.json().await?;
println!("{}", group["NmDisp"]); // "Nateni"
println!("{}", group["Ctry"]); // "Benin"
println!("{}", group["LPIname"]); // "Pioneer Unreached People Group"
Ok(())
}
Page through all people groups
use reqwest::Client;
use serde_json::Value;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let base_url = "https://peoplegroups.org/wp-json/pg/v1/people-groups";
let mut page = 1u32;
let mut total_pages = 1u32;
let mut all_groups: Vec<Value> = Vec::new();
loop {
let response = client
.get(base_url)
.query(&[("page", page.to_string()), ("per_page", "250".to_string())])
.send()
.await?;
total_pages = response
.headers()
.get("X-WP-TotalPages")
.and_then(|v| v.to_str().ok())
.and_then(|v| v.parse().ok())
.unwrap_or(1);
let batch: Vec<Value> = response.json().await?;
all_groups.extend(batch);
if page >= total_pages {
break;
}
page += 1;
}
println!("Fetched {} people groups", all_groups.len());
Ok(())
}
Using the API - Real-world Use Cases
Plot people groups on an interactive map
Every record includes Latitude and Longitude fields.
Fetch a page of groups and drop markers on a Mapbox GL JS or
Leaflet map,
colored by lostness priority or evangelical status.
// Mapbox GL JS — add people group markers from the API
const response = await fetch(
"https://peoplegroups.org/wp-json/pg/v1/people-groups?per_page=250"
);
const groups = await response.json();
// Build a GeoJSON FeatureCollection
const geojson = {
type: "FeatureCollection",
features: groups
.filter(g => g.Latitude && g.Longitude)
.map(g => ({
type: "Feature",
geometry: {
type: "Point",
coordinates: [parseFloat(g.Longitude), parseFloat(g.Latitude)],
},
properties: {
pgid: g.PGID,
name: g.NmDisp,
country: g.Ctry,
lpi: g.LPI,
lpiName: g.LPIname,
pop: g.Pop,
religion: g.Rlgn,
},
})),
};
// Add as a Mapbox source and layer
map.addSource("people-groups", { type: "geojson", data: geojson });
map.addLayer({
id: "people-groups-circles",
type: "circle",
source: "people-groups",
paint: {
// Color by LPI: 1 (pioneer) = red, higher = orange/yellow
"circle-color": [
"interpolate", ["linear"], ["get", "lpi"],
1, "#e63946",
2, "#f4a261",
3, "#2a9d8f",
],
"circle-radius": 6,
"circle-opacity": 0.85,
},
});
// Show a popup on click
map.on("click", "people-groups-circles", (e) => {
const p = e.features[0].properties;
new mapboxgl.Popup()
.setLngLat(e.lngLat)
.setHTML(`
<strong>${p.name}</strong> of ${p.country}<br>
Population: ${p.pop?.toLocaleString() ?? "Unknown"}<br>
Religion: ${p.religion}<br>
Status: ${p.lpiName}
`)
.addTo(map);
});
Build a people group dropdown for an application
Populate a <select> element with people group names, using PGID as the value.
Filter by country to scope the list for a specific region.
async function populatePeopleGroupSelect(selectElement, countryFilter = null) {
let page = 1;
let totalPages = 1;
const groups = [];
do {
const response = await fetch(
`https://peoplegroups.org/wp-json/pg/v1/people-groups?page=${page}&per_page=250`
);
const batch = await response.json();
groups.push(...batch);
totalPages = parseInt(response.headers.get("X-WP-TotalPages") ?? "1", 10);
page++;
} while (page <= totalPages);
const filtered = countryFilter
? groups.filter(g => g.Ctry === countryFilter)
: groups;
filtered.sort((a, b) => a.NmDisp.localeCompare(b.NmDisp));
selectElement.innerHTML = '<option value="">-- Select a People Group --</option>';
for (const group of filtered) {
const option = document.createElement("option");
option.value = group.PGID;
option.textContent = `${group.NmDisp} (${group.Ctry})`;
selectElement.appendChild(option);
}
}
// Usage
const select = document.getElementById("people-group-select");
await populatePeopleGroupSelect(select, "Nigeria");
select.addEventListener("change", async (e) => {
if (!e.target.value) return;
const response = await fetch(
`https://peoplegroups.org/wp-json/pg/v1/people-groups/${e.target.value}`
);
const group = await response.json();
console.log(group);
});
Display a people group profile card
Fetch a single record by PGID and render a profile card with key statistics, church status, and a photo if available.
async function renderProfileCard(pgid, containerElement) {
const response = await fetch(
`https://peoplegroups.org/wp-json/pg/v1/people-groups/${pgid}`
);
if (!response.ok) {
containerElement.innerHTML = "<p>People group not found.</p>";
return;
}
const g = await response.json();
containerElement.innerHTML = `
<div class="pg-card">
${g.PicURL ? `<img src="${g.PicURL}" alt="${g.NmDisp}" />` : ""}
<h2>${g.NmDisp} of ${g.Ctry}</h2>
<p class="pg-pgid">PGID: ${g.PGID}</p>
<ul class="pg-stats">
<li><strong>Population:</strong> ${g.Pop?.toLocaleString() ?? "Unknown"}</li>
<li><strong>Religion:</strong> ${g.Rlgn}</li>
<li><strong>Language:</strong> ${g.Lang}</li>
<li><strong>Region:</strong> ${g.RegnSub}, ${g.Regn}</li>
<li><strong>Evangelical Level:</strong> ${g.EvngLvl}</li>
<li><strong>Church Planting:</strong> ${g.Plnting}</li>
<li><strong>Engagement Status:</strong> ${g.EngStat}</li>
</ul>
<div class="pg-lostness">
<strong>${g.LPIname}</strong>
<p>${g.LPIdesc}</p>
</div>
<div class="pg-resources">
<strong>Resources Available:</strong>
Bible: ${g.Bible} |
Jesus Film: ${g.Jesus} |
Total: ${g.ResTot}
</div>
${g.PicCrdt ? `<p class="pg-photo-credit">${g.PicCrdt}</p>` : ""}
</div>
`;
}
// Usage
renderProfileCard("PG012345", document.getElementById("pg-profile"));
Generate a prayer guide entry
Pull lostness and church status fields to generate structured prayer prompts for a daily or weekly prayer guide application.
import requests
from datetime import date
def build_prayer_entry(pgid: str) -> dict:
response = requests.get(
f"https://peoplegroups.org/wp-json/pg/v1/people-groups/{pgid}"
)
response.raise_for_status()
g = response.json()
return {
"date": date.today().isoformat(),
"group": g["NmDisp"],
"country": g["Ctry"],
"population": g.get("Pop"),
"religion": g["Rlgn"],
"language": g["Lang"],
"status": g.get("LPIname", "Unknown"),
"church_status": g.get("GSEClng", ""),
"engagement": g.get("SPIdesc", ""),
"prompts": [
f"Pray for the {g['NmDisp']} people of {g['Ctry']}, "
f"a community of {g.get('Pop', 0):,} primarily practicing {g['Rlgn']}.",
f"Their status: {g.get('LPIdesc', '')}",
f"Church situation: {g.get('GSEClng', '')}",
"Ask God to raise up workers and open doors for the gospel among this people.",
],
}
# Usage
entry = build_prayer_entry("PG012345")
for prompt in entry["prompts"]:
print(prompt)
Filter for unreached people groups
Use evangelical level and lostness priority fields to identify unreached groups within a specific region, country, or religion family.
import requests
def get_unreached_groups(country: str = None, religion: str = None) -> list:
base_url = "https://peoplegroups.org/wp-json/pg/v1/people-groups"
page = 1
total_pages = 1
results = []
while page <= total_pages:
response = requests.get(base_url, params={"page": page, "per_page": 250})
response.raise_for_status()
for group in response.json():
if group.get("EvngLvl") != "Less than 2%":
continue
if country and group.get("Ctry") != country:
continue
if religion and group.get("Rlgn") != religion:
continue
results.append(group)
total_pages = int(response.headers.get("X-WP-TotalPages", 1))
page += 1
return results
# All unreached groups in Nigeria
nigeria_unreached = get_unreached_groups(country="Nigeria")
print(f"{len(nigeria_unreached)} unreached people groups in Nigeria")
# All unreached Muslim groups
muslim_unreached = get_unreached_groups(religion="Islam")
print(f"{len(muslim_unreached)} unreached Muslim people groups worldwide")
Build a country summary dashboard
Aggregate people group data by country to display a summary of unreached groups, total population, and engagement status — useful for a missions strategy dashboard.
async function buildCountrySummary(countryCode) {
let page = 1;
let totalPages = 1;
const groups = [];
do {
const response = await fetch(
`https://peoplegroups.org/wp-json/pg/v1/people-groups?page=${page}&per_page=250`
);
const batch = await response.json();
groups.push(...batch.filter(g => g.ISOalpha3 === countryCode));
totalPages = parseInt(response.headers.get("X-WP-TotalPages") ?? "1", 10);
page++;
} while (page <= totalPages);
const unreached = groups.filter(g => g.EvngLvl === "Less than 2%");
const totalPop = groups.reduce((sum, g) => sum + (parseInt(g.Pop) || 0), 0);
const engaged = groups.filter(g => g.EngStat === "Engaged");
return {
country: groups[0]?.Ctry ?? countryCode,
totalGroups: groups.length,
unreachedCount: unreached.length,
totalPopulation: totalPop,
engagedCount: engaged.length,
religionBreakdown: groups.reduce((acc, g) => {
acc[g.Rlgn] = (acc[g.Rlgn] ?? 0) + 1;
return acc;
}, {}),
};
}
// Usage
const summary = await buildCountrySummary("ETH"); // Ethiopia
console.log(summary);
Field Reference
| Field | Type | Description |
|---|---|---|
PGID | string | People Group ID — primary identifier. |
PEID | integer | People Group Entity ID. |
NmDisp | string | Display name. |
NmAlt | string | Alternate names. |
ISOalpha3 | string | ISO 3166-1 alpha-3 country code. |
Ctry | string | Country name. |
Regn | string | UN region. |
RegnSub | string | UN sub-region. |
Latitude | number | Latitude (decimal degrees). |
Longitude | number | Longitude (decimal degrees). |
Pop | integer | Population estimate. |
ROL | string | ISO 639-3 language code. |
Lang | string | Primary language name. |
LangFamily | string | Language family. |
LangSpkrs | integer | Global language speakers. |
ROR | string | Religion of record code. |
Rlgn | string | Primary religion name. |
RlgnDiv | string | Religion display name. |
EvngLvl | string | Evangelical level description. |
CongExst | string | Whether congregations exist. |
Plnting | string | Church planting activity (last 2 years). |
EngStat | string | Engagement status. |
GSEC | integer | Great Commission Status of Evangelization code (1–6). |
GSECbrf | string | GSEC brief description. |
GSEClng | string | GSEC long description. |
SPI | integer | Strategic Priority Index (Engagement Progress). |
SPIdesc | string | SPI description. |
LPI | integer | Lostness Priority Index. |
LPIname | string | LPI name. |
LPIdesc | string | LPI description. |
Affbloc | string | ROP1 Affinity Bloc name. |
PplClstr | string | People cluster name. |
PplNm | string | ROP3 people name. |
Ethne | string | ROP25 ethnographic group name. |
Bible | string | Bible availability. |
Jesus | string | Jesus Film availability. |
ResTot | integer | Total evangelical resources available. |
PeopleDesc | string | People group description. |
LocationDesc | string | Location description. |
PicURL | string | Photo URL. |
PicCrdt | string | Photo credit. |
UpdatedDate | string | Last updated (ISO 8601). |
Data Standards
PeopleGroups.org data is built on the Harvest Information Standards (HIS), a cooperative effort of several organizations to make people group information shareable between mission agencies. Alongside HIS, the API uses the international coding standards below, so that data pulled from this API can be joined to other datasets without translation.
| Standard | Used for | API fields |
|---|---|---|
| Registry of Peoples (ROP) | People group identity at four levels — affinity bloc, people cluster, ethnographic group and people | Affbloc, PplClstr, Ethne, PplNm |
| ISO 639-3 | Language identification | ROL, Lang |
| ISO 3166-1 | Country identification, in alpha-2, alpha-3 and numeric forms | ISOalpha3, Ctry |
| UN M49 | Geographic regions and sub-regions | Regn, RegnSub |
| Religion of Record (ROR) | Religious affiliation | ROR, Rlgn, RlgnDiv |
Country records additionally carry GENC (Geopolitical Entities, Names, and Codes) and FIPS 10-4 codes, for compatibility with partner datasets that have not moved to ISO 3166.
PeopleGroups.org identifiers
Two fields are ours rather than an external standard: the People Group ID (PGID), the primary identifier for a people group, and the People Group Entity ID (PEID). Use the PGID when linking back to this site; use the ROP codes when exchanging data with other organizations. We encourage organizations to code their own people group information to ROP codes — see the Registry of Peoples.
