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). |
