Areas
An area limits a feed to a part of the world. It is a name and a list of places (a city, a county, a state, a country, anywhere in the world) and circles (a point and a radius). An item is in the area when it is in any of them.
Two processors, one area
An area is used through two processors, each holding its own copy of the places:
- Area match (
area_match) decides by boundaries, with no AI. Its outputmatchismatch(inside the area),no_match(outside it) orunknown(the item has no known location). - Area relevance (
area_relevance) is AI: it judges from the item's own text whether the item takes place in or is about the area. Its output isanswer(yesorno) with aconfidence.
An item with no known location is unknown to area match, so it is not in
the area by place: a filter on match = "match" alone (or in_area without
areaRelevance) leaves it out. With area relevance, that processor judges it
from its text. To keep such items by place, write your own filter on
match != "no_match".
Use them together in an include, as any of the two: the boundary decides
what it can, and area relevance catches what boundaries miss, such as an
event whose venue the map placed in a town of the same name elsewhere, or a
story about the region that names no place inside it.
{
"any": [
{
"processor": "AREA_MATCH_ID",
"output": "match",
"op": "=",
"value": "match"
},
{
"processor": "AREA_RELEVANCE_ID",
"output": "answer",
"op": "=",
"value": "yes"
}
]
}
Our opt-in filters do this for you: in_area for
event feeds, and news and creator_posts for news feeds, each taking
areaMatch and, optionally, areaRelevance. A feed that should keep strictly
to the boundaries leaves area relevance out.
Keep the two processors' places the same. We do not copy one to the
other or check that they agree. If they differ, an item can be in the area
by one and out of it by the other, and the any lets in what either says:
the feed then covers both areas. When you change the places, change both.
Area processors run on events and news by default (itemTypes), so one area
serves all your feeds.
How area match decides
- Events are matched by their venue when they have one: the event is in the area when its venue is inside one of the places or circles. An event with no venue that names a region is in the area when that region is one of the places or lies inside one, or overlaps a circle. See How geography decides what a feed carries.
- News items are matched by the places they are about: one of them must lie inside the area. A story about a whole state or country does not match a county by place; it reaches the feed only through area relevance.
A region that only contains one of the area's places (a whole state around your county) is not in the area by place.
Places
definition.area.places holds 1 to 50 entries, each one of:
- A place by name:
{ "name": "Gaston County, North Carolina" }, with an optionallevel. The name is resolved when you write it: it resolves when exactly one place at that level has a full name starting with it (ignoring case).Gaston CountyandGaston County, North Carolinaboth work when only one place matches. - A place by id:
{ "id": "mbx_…" }, theidof a place a response returned: a place of one of your areas, or a candidate from a refusal. - A circle:
{ "center": { "lat": 34.925, "lng": -81.025 }, "radiusKm": 15, "label": "Within 15 km of Rock Hill" }. The radius is above 0 and at most 500 km. - A place or circle as a response returned it, with its
type: a place is kept by itsid(its other fields are ignored) and a circle is read as written. So you can send a definition you read back unchanged.
Two entries that resolve to the same place are refused: the later one is a
422 AREA_PLACES_UNRESOLVED failure with no candidates, as is an id we
did not issue (see When a name does not resolve).
Levels
level | What it is |
|---|---|
country | A country |
region | A state, province or similar |
district | The default. A county in the United States |
place | A city or town |
locality | A part of a city or town |
neighborhood | A neighborhood |
A name resolves only at its level. A city needs "level": "place"; without
it, the name is looked up as a county.
Places in a response
A response returns the area with each place resolved:
{
"name": "Charlotte metro",
"places": [
{
"type": "place",
"id": "mbx_WyJkWEp1T20xaWVIQnNZenBCVmtKdk4xRSIsImRpc3RyaWN0IiwiTWVja2xlbmJ1cmcgQ291bnR5LCBOb3J0aCBDYXJvbGluYSwgVW5pdGVkIFN0YXRlcyJd",
"name": "Mecklenburg County",
"level": "district",
"displayText": "Mecklenburg County, North Carolina, United States",
"geo": {
"center": { "lat": 35.247, "lng": -80.833 },
"bbox": [-81.058, 35.002, -80.55, 35.515]
}
},
{
"type": "circle",
"center": { "lat": 34.925, "lng": -81.025 },
"radiusKm": 15,
"label": "Within 15 km of Rock Hill"
}
]
}
| Field | Description |
|---|---|
type | place or circle |
id | The place's stable id. Send { "id": … } to keep the place without resolving its name again |
name | The place's name |
level | Its level |
displayText | The place's full name. It is longer than the displayText of a news item's about.places (Sterling Heights, MI) |
geo | center (lat, lng) and bbox ([west, south, east, north]); each null when unknown |
Changing an area's places
PATCH /processors/{id} with the whole new definition (see
Changing a processor). Send the places
you keep as { "id": … } (or exactly as the processor's last response
returned them), and new ones by name:
{
"name": "Charlotte metro",
"definition": {
"area": {
"name": "Charlotte metro",
"places": [
{
"id": "mbx_WyJkWEp1T20xaWVIQnNZenBCVmtKdk4xRSIsImRpc3RyaWN0IiwiTWVja2xlbmJ1cmcgQ291bnR5LCBOb3J0aCBDYXJvbGluYSwgVW5pdGVkIFN0YXRlcyJd"
},
{ "name": "Union County, North Carolina" }
]
}
}
}
The change makes a new version of the processor and applies going forward. Make the same change to the area's other processor.
When a name does not resolve
A name that matches no place at its level, or several, is refused with 422
AREA_PLACES_UNRESOLVED. Nothing is saved. details.failures lists each
failing entry by its index in places, with up to ten candidates: for an
ambiguous name, the places at its level it could mean; for a name with no
match at its level, what the search found at other levels. The same code,
with candidates: [], refuses an entry that resolves to a place an earlier
entry already named, and an id we did not issue or that is malformed. An
id from a candidate list whose place can no longer be found lists the places
now found at its level. In this example, two of the candidates are shown and
their ids are shortened:
{
"success": false,
"error": {
"code": "AREA_PLACES_UNRESOLVED",
"message": "1 of the area's places did not resolve to exactly one place: \"Union County\". Send a candidate's `id` as `{ \"id\": … }`, or a more specific name.",
"details": {
"failures": [
{
"index": 1,
"name": "Union County",
"candidates": [
{
"id": "mbx_…",
"name": "Union County",
"level": "district",
"displayText": "Union County, North Carolina, United States"
},
{
"id": "mbx_…",
"name": "Union County",
"level": "district",
"displayText": "Union County, South Carolina, United States"
}
]
}
]
}
}
}
Send the candidate you mean back as { "id": … }, or a longer name such as
Union County, North Carolina. If place search is unavailable, the request
is a 503 SERVICE_UNAVAILABLE with a Retry-After header.
Place search limit
A POST /processors or PATCH /processors/{id} that searches places counts
against your organization's place-search limit of 30 requests a minute,
once per request however many places it names. A request searches when it
gives a place by name, or by an id none of your areas holds. One that searches
nothing (no area, only circles, or only places your areas already hold, sent
back by id) does not count, and neither does a request refused before any
search (a taken name, an invalid body). Over the limit the request is a 429
RATE_LIMITED with a Retry-After header (seconds to wait); nothing is
searched or saved. When the limit cannot be counted, the request is a 503
SERVICE_UNAVAILABLE with a Retry-After header.
Creating an area
Create the area match, then (if you want it) the area relevance processor with the same places:
{
"template": "area_match",
"name": "Charlotte metro",
"definition": {
"area": {
"name": "Charlotte metro",
"places": [
{ "name": "Mecklenburg County, North Carolina" },
{ "name": "Gaston County, North Carolina" },
{ "name": "Rock Hill, South Carolina", "level": "place" }
]
}
}
}
{
"template": "area_relevance",
"name": "Charlotte metro (relevance)",
"definition": {
"area": {
"name": "Charlotte metro",
"places": [
{ "name": "Mecklenburg County, North Carolina" },
{ "name": "Gaston County, North Carolina" },
{ "name": "Rock Hill, South Carolina", "level": "place" }
]
}
}
}
Both go to POST /processors. Processor names are unique in your
organization, so the two need different names. Once the first is created,
you can send its resolved places' ids to the second instead of the names.