CPIR Parent Center Directory API

Public, unauthenticated read access to the national Parent Center directory - the machine-readable form of the Find Your Parent Center finder. Each entry is a Parent Training and Information Center (PTI), Community Parent Resource Center (CPRC) or regional Parent Technical Assistance Center, carrying organization name, work address, geocoded latitude and longitude, telephone numbers, email addresses and a public link, in vCard/hCard field naming. Verified live on 2026-09-05 at 784+ published entries across 55 state and territory categories. Anonymous access is read-only; the collection answers Allow GET without credentials, and CPIR issues no API keys.

Operations 15

GET /cn-api/v1 Directory namespace index #
GET /cn-api/v1/entry List Parent Center directory entries #
POST /cn-api/v1/entry Create a directory entry (authenticated) #
GET /cn-api/v1/entry/{id} Retrieve one Parent Center #
PUT /cn-api/v1/entry/{id} Update a directory entry (authenticated) #
DELETE /cn-api/v1/entry/{id} Delete a directory entry (authenticated) #
POST /cn-api/v1/entry/{id}/moderate Approve or unapprove an entry (authenticated) #
GET /cn-api/v1/category List directory categories #
GET /cn-api/v1/category/{id} Retrieve one directory category #
GET /cn-api/v1/autocomplete/{type} Type-ahead lookup over a directory field #
GET /cn-api/v1/recently_viewed List recently viewed entries #
POST /cn-api/v1/account/login Visitor account sign-in #
POST /cn-api/v1/account/register Visitor account registration #
POST /cn-api/v1/account/request-reset-password Request a password reset #
POST /cn-api/v1/account/reset-password Complete a password reset #

Documentation

Specifications

Other Resources

🔗
Overlay
https://raw.githubusercontent.com/api-evangelist/center-for-parent-information-and-resources/refs/heads/main/overlays/center-for-parent-information-and-resources-parent-center-directory-api-overlay.yaml
🔗
Conventions
https://raw.githubusercontent.com/api-evangelist/center-for-parent-information-and-resources/refs/heads/main/conventions/center-for-parent-information-and-resources-conventions.yml
🔗
ErrorCatalog
https://raw.githubusercontent.com/api-evangelist/center-for-parent-information-and-resources/refs/heads/main/errors/center-for-parent-information-and-resources-problem-types.yml
🔗
Lifecycle
https://raw.githubusercontent.com/api-evangelist/center-for-parent-information-and-resources/refs/heads/main/lifecycle/center-for-parent-information-and-resources-lifecycle.yml
🔗
DataModel
https://raw.githubusercontent.com/api-evangelist/center-for-parent-information-and-resources/refs/heads/main/data-model/center-for-parent-information-and-resources-data-model.yml
🔗
Conformance
https://raw.githubusercontent.com/api-evangelist/center-for-parent-information-and-resources/refs/heads/main/conformance/center-for-parent-information-and-resources-conformance.yml
🔗
AgentSkill
https://raw.githubusercontent.com/api-evangelist/center-for-parent-information-and-resources/refs/heads/main/skills/_index.yml
🔗
X-MCPServerCandidate
https://raw.githubusercontent.com/api-evangelist/center-for-parent-information-and-resources/refs/heads/main/mcp/center-for-parent-information-and-resources-mcp.yml
🔗
APIsJSON
https://raw.githubusercontent.com/api-evangelist/center-for-parent-information-and-resources/refs/heads/main/apis.yml

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/cpir-parent-center-directory-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

center-for-parent-information-and-resources-parent-center-directory-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: CPIR Parent Center Directory API
  version: cn-api/v1
  description: >-
    Public, unauthenticated read access to the Center for Parent Information and Resources (CPIR)
    national Parent Center directory — the machine-readable form of the "Find Your Parent Center"
    finder at parentcenterhub.org/find-your-center/. Each entry is a Parent Training and Information
    Center (PTI), Community Parent Resource Center (CPRC) or regional Parent Technical Assistance
    Center, carrying its organization name, work address, geocoded latitude/longitude, telephone
    numbers, email addresses, social profiles and public web link. Verified live on 2026-09-05 at
    784+ published entries across 55 state and territory categories.

    Anonymous access is read-only: the collection answers `Allow: GET` without credentials. The
    POST/PUT/PATCH/DELETE methods declared on the same routes, and the `/settings` routes, require
    an authenticated WordPress session and return `401 rest_forbidden` anonymously — they are
    documented here as gated, not as a public surface.
  contact:
    name: Center for Parent Information and Resources
    url: https://www.parentcenterhub.org/contact/
  x-derived-from: https://www.parentcenterhub.org/wp-json/ route index (Connections Business Directory `cn-api/v1` namespace), plus live anonymous GET verification of each documented path
  x-derived-on: '2026-09-05'
  x-api-evangelist-note: >-
    Third-party profile. CPIR publishes no developer program and no OpenAPI of its own; this documents
    the anonymously readable directory API that its own site serves. Every path, parameter, enum and
    default here was read from the server's published route-index schema on 2026-09-05 — nothing is
    invented. Response shapes were read from live 200 responses. The `/account/*` routes are
    transcribed from the route index only and were deliberately NOT invoked.
servers:
  - url: https://www.parentcenterhub.org/wp-json
    description: CPIR WordPress REST API (Parent Center Hub)
tags:
  - name: Entries
    description: Parent Center directory records — PTIs, CPRCs and regional Parent Technical Assistance Centers.
  - name: Categories
    description: The state and territory taxonomy the directory is organised by.
  - name: Autocomplete
    description: Type-ahead lookup over directory fields, backing the finder's search form.
  - name: Account
    description: Visitor account routes exposed by the directory plugin. Gated behind a request nonce; not invoked during profiling.
paths:
  /cn-api/v1:
    get:
      operationId: getDirectoryNamespaceIndex
      summary: Directory namespace index
      description: Route index for the `cn-api/v1` namespace, listing every directory route with its argument schema.
      tags: [Entries]
      parameters:
        - name: context
          in: query
          description: Scope under which the request is made; determines fields present in response.
          schema: { type: string, default: view }
      responses:
        '200':
          description: Namespace route index.
          content:
            application/json:
              schema: { type: object }
  /cn-api/v1/entry:
    get:
      operationId: listDirectoryEntries
      summary: List Parent Center directory entries
      description: >-
        Retrieve a paginated collection of published Parent Center records. Verified live at 784+
        entries on 2026-09-05 paging at `per_page=100`.
      tags: [Entries]
      parameters:
        - name: context
          in: query
          description: Scope under which the request is made; determines fields present in response.
          schema: { type: string, default: view, enum: [view, embed, edit] }
        - name: page
          in: query
          description: Current page of the collection.
          schema: { type: integer, default: 1 }
        - name: per_page
          in: query
          description: Maximum number of items to be returned in result set.
          schema: { type: integer, default: 10 }
        - name: search
          in: query
          description: Limit results to those matching a string.
          schema: { type: string }
        - name: exclude
          in: query
          description: Ensure result set excludes specific IDs.
          schema: { type: array, items: { type: integer }, default: [] }
        - name: include
          in: query
          description: Limit result set to specific IDs.
          schema: { type: array, items: { type: integer }, default: [] }
        - name: offset
          in: query
          description: Offset the result set by a specific number of items.
          schema: { type: integer }
        - name: tax_relation
          in: query
          description: Limit result set based on relationship between multiple taxonomies.
          schema: { type: string, enum: [AND, OR] }
        - name: categories
          in: query
          description: Limit result set to items with specific terms assigned in the categories taxonomy.
          schema: { type: array, items: { type: integer }, default: [] }
        - name: categories_exclude
          in: query
          description: Limit result set to items except those with specific terms assigned in the categories taxonomy.
          schema: { type: array, items: { type: integer }, default: [] }
      responses:
        '200':
          description: A collection of directory entries.
          content:
            application/json:
              schema:
                type: array
                items: { $ref: '#/components/schemas/Entry' }
        '400':
          $ref: '#/components/responses/InvalidParam'
    post:
      operationId: createDirectoryEntry
      summary: Create a directory entry (authenticated)
      description: >-
        Declared on the route index. Requires an authenticated WordPress session with directory
        capabilities; anonymous callers receive `401 rest_forbidden`. Not part of the public surface.
      tags: [Entries]
      security:
        - cookieNonce: []
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/EntryWrite' }
      responses:
        '201':
          description: Entry created.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Entry' }
        '401':
          $ref: '#/components/responses/Forbidden'
  /cn-api/v1/entry/{id}:
    parameters:
      - name: id
        in: path
        required: true
        description: Unique identifier for the entry.
        schema: { type: integer }
    get:
      operationId: getDirectoryEntry
      summary: Retrieve one Parent Center
      description: Retrieve a single published Parent Center record by its numeric identifier.
      tags: [Entries]
      parameters:
        - name: context
          in: query
          description: Scope under which the request is made; determines fields present in response.
          schema: { type: string, default: view, enum: [view, embed, edit] }
      responses:
        '200':
          description: A single directory entry.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Entry' }
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      operationId: updateDirectoryEntry
      summary: Update a directory entry (authenticated)
      description: Declared on the route index; requires an authenticated session. Anonymous callers receive `401 rest_forbidden`.
      tags: [Entries]
      security:
        - cookieNonce: []
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/EntryWrite' }
      responses:
        '200':
          description: Entry updated.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Entry' }
        '401':
          $ref: '#/components/responses/Forbidden'
    delete:
      operationId: deleteDirectoryEntry
      summary: Delete a directory entry (authenticated)
      description: >-
        Declared on the route index; requires an authenticated session. The route index states
        `force` must be true because the resource does not support trashing — there is therefore no
        published restore path for a deleted entry.
      tags: [Entries]
      security:
        - cookieNonce: []
      parameters:
        - name: force
          in: query
          description: Required to be true, as resource does not support trashing.
          schema: { type: boolean, default: false }
      responses:
        '200':
          description: Entry deleted.
        '401':
          $ref: '#/components/responses/Forbidden'
  /cn-api/v1/entry/{id}/moderate:
    post:
      operationId: moderateDirectoryEntry
      summary: Approve or unapprove an entry (authenticated)
      description: >-
        Declared on the route index; requires an authenticated session with moderation capability.
        This is the reversal path for directory submissions — `approve` and `unapprove` are inverse
        actions on the same record. No time window is published for it.
      tags: [Entries]
      security:
        - cookieNonce: []
      parameters:
        - name: id
          in: path
          required: true
          description: Unique identifier for the entry.
          schema: { type: integer }
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  type: string
                  enum: [approve, unapprove]
                  description: Moderation action to apply.
      responses:
        '200':
          description: Moderation applied.
        '401':
          $ref: '#/components/responses/Forbidden'
  /cn-api/v1/category:
    get:
      operationId: listDirectoryCategories
      summary: List directory categories
      description: >-
        Retrieve the taxonomy the directory is organised by. Verified live on 2026-09-05 at 55 terms —
        the 50 US states plus territories and national groupings.
      tags: [Categories]
      parameters:
        - name: context
          in: query
          schema: { type: string, default: view }
          description: Scope under which the request is made; determines fields present in response.
        - name: page
          in: query
          schema: { type: integer, default: 1 }
          description: Current page of the collection.
        - name: per_page
          in: query
          schema: { type: integer, default: 10 }
          description: Maximum number of items to be returned in result set.
        - name: search
          in: query
          schema: { type: string }
          description: Limit results to those matching a string.
      responses:
        '200':
          description: A collection of category terms.
          content:
            application/json:
              schema:
                type: array
                items: { $ref: '#/components/schemas/Category' }
  /cn-api/v1/category/{id}:
    get:
      operationId: getDirectoryCategory
      summary: Retrieve one directory category
      description: Retrieve a single state or territory term by its numeric identifier.
      tags: [Categories]
      parameters:
        - name: id
          in: path
          required: true
          description: Unique identifier for the term.
          schema: { type: integer }
        - name: context
          in: query
          schema: { type: string, default: view }
          description: Scope under which the request is made; determines fields present in response.
      responses:
        '200':
          description: A single category term.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Category' }
        '404':
          $ref: '#/components/responses/NotFound'
  /cn-api/v1/autocomplete/{type}:
    get:
      operationId: autocompleteDirectoryField
      summary: Type-ahead lookup over a directory field
      description: >-
        Search one indexed directory field and return matching raw records. Backs the finder's
        search form. Verified live on 2026-09-05 for `organization` and `city`.
      tags: [Autocomplete]
      parameters:
        - name: type
          in: path
          required: true
          description: The directory field to search.
          schema:
            type: string
            enum: [name, last_name, title, organization, department, district, county, city, state, zipcode, country]
        - name: context
          in: query
          description: Scope under which the request is made; determines fields present in response.
          schema: { type: string, default: view }
        - name: page
          in: query
          description: Current page of the collection.
          schema: { type: integer, default: 1 }
        - name: per_page
          in: query
          description: Maximum number of items to be returned in result set.
          schema: { type: integer, default: 10 }
        - name: search
          in: query
          description: Limit results to those matching a string.
          schema: { type: string }
        - name: offset
          in: query
          description: Offset the result set by a specific number of items.
          schema: { type: integer }
        - name: order
          in: query
          description: Order sort attribute ascending or descending.
          schema: { type: string, default: asc, enum: [asc, desc] }
        - name: orderby
          in: query
          description: Sort collection by resource attribute.
          schema:
            type: string
            default: name
            enum: [id, include, name, slug, term_group, description, count]
      responses:
        '200':
          description: Matching raw directory rows for the requested field.
          content:
            application/json:
              schema: { type: array, items: { type: object } }
  /cn-api/v1/recently_viewed:
    get:
      operationId: listRecentlyViewedEntries
      summary: List recently viewed entries
      description: >-
        Session-scoped list of entries the current visitor has viewed. Returns an empty array for an
        anonymous caller with no session (verified live 2026-09-05).
      tags: [Entries]
      responses:
        '200':
          description: Recently viewed entries for this session.
          content:
            application/json:
              schema: { type: array, items: { $ref: '#/components/schemas/Entry' } }
  /cn-api/v1/account/login:
    post:
      operationId: accountLogin
      summary: Visitor account sign-in
      description: >-
        Transcribed from the published route index; NOT invoked during profiling. Requires a
        `_cnonce` request token issued by the site, so it is not callable as a standalone API.
      tags: [Account]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [_cnonce, log, pwd]
              properties:
                _cnonce: { type: string, description: The request token. }
                log: { type: string, description: Username or email. }
                pwd: { type: string, description: Password. }
                redirect_to: { type: string, description: The URL to redirect to after form submission. }
                rememberme: { type: string, enum: ['0', '1'], description: Remember Me }
      responses:
        '200':
          description: Sign-in result.
  /cn-api/v1/account/register:
    post:
      operationId: accountRegister
      summary: Visitor account registration
      description: Transcribed from the published route index; NOT invoked during profiling. Requires a `_cnonce` request token.
      tags: [Account]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [_cnonce, user_login, user_email]
              properties:
                _cnonce: { type: string, description: The request token. }
                user_login: { type: string, description: Username. }
                user_email: { type: string, description: Email. }
                redirect_to: { type: string, description: The URL to redirect to after form submission. }
      responses:
        '200':
          description: Registration result.
  /cn-api/v1/account/request-reset-password:
    post:
      operationId: accountRequestResetPassword
      summary: Request a password reset
      description: Transcribed from the published route index; NOT invoked during profiling. Requires a `_cnonce` request token.
      tags: [Account]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [_cnonce, log]
              properties:
                _cnonce: { type: string, description: The request token. }
                log: { type: string, description: Username or email. }
                redirect_to: { type: string, description: The URL to redirect to after form submission. }
      responses:
        '200':
          description: Reset request result.
  /cn-api/v1/account/reset-password:
    post:
      operationId: accountResetPassword
      summary: Complete a password reset
      description: Transcribed from the published route index; NOT invoked during profiling. Requires a `_cnonce` request token and a reset key.
      tags: [Account]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [_cnonce, pass1, pass2, key]
              properties:
                _cnonce: { type: string, description: The request token. }
                pass1: { type: string, description: New password. }
                pass2: { type: string, description: Confirm password. }
                pw_weak: { type: boolean, description: Confirm use of weak password. }
                key: { type: string, description: Password reset key. }
                redirect_to: { type: string, description: The URL to redirect to after form submission. }
      responses:
        '200':
          description: Reset result.
components:
  securitySchemes:
    cookieNonce:
      type: apiKey
      in: header
      name: X-WP-Nonce
      description: >-
        WordPress cookie authentication paired with an `X-WP-Nonce` header. The server advertises
        `Access-Control-Allow-Headers: Authorization, X-WP-Nonce, Content-Disposition, Content-MD5,
        Content-Type`. Read routes documented here need no credential.
  responses:
    Forbidden:
      description: The caller is not authenticated or lacks the capability for this route.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    NotFound:
      description: No resource matched the requested identifier.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    InvalidParam:
      description: One or more request parameters failed validation.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
  schemas:
    Error:
      type: object
      description: >-
        The WordPress REST error envelope, observed live on this host. Not RFC 9457 —
        the media type is `application/json`, not `application/problem+json`.
      properties:
        code: { type: string, description: Machine-readable error slug, e.g. `rest_forbidden`. }
        message: { type: string, description: Human-readable message. }
        data:
          type: object
          properties:
            status: { type: integer, description: The HTTP status code, repeated in the body. }
    Rendered:
      type: object
      description: A rendered text field. WordPress returns display strings HTML-entity encoded.
      properties:
        rendered: { type: string }
    Address:
      type: object
      description: A postal address attached to an entry, in vCard/hCard field naming.
      properties:
        id: { type: integer }
        order: { type: integer }
        preferred: { type: boolean }
        type: { type: string, description: 'Address kind, e.g. `work`.' }
        street_address: { $ref: '#/components/schemas/Rendered' }
        extended_address: { $ref: '#/components/schemas/Rendered' }
        extended_address_2: { $ref: '#/components/schemas/Rendered' }
        extended_address_3: { $ref: '#/components/schemas/Rendered' }
        district: { $ref: '#/components/schemas/Rendered' }
        county: { $ref: '#/components/schemas/Rendered' }
        locality: { $ref: '#/components/schemas/Rendered' }
        region: { $ref: '#/components/schemas/Rendered' }
        postal_code: { $ref: '#/components/schemas/Rendered' }
        country_name: { $ref: '#/components/schemas/Rendered' }
        latitude: { type: string, description: Decimal latitude as a string. }
        longitude: { type: string, description: Decimal longitude as a string. }
        visibility: { type: string, enum: [public, private, unlisted] }
    Entry:
      type: object
      description: >-
        A Parent Center directory record. Field naming follows the vCard/hCard vocabulary
        (`fn`, `adr`, `tel`, `email`, `org`, `honorific_prefix`, `honorific_suffix`).
      properties:
        id: { type: integer }
        type: { type: string, enum: [family, individual, organization] }
        link: { type: string, format: uri, description: Public page for this Parent Center on parentcenterhub.org. }
        slug: { type: string }
        fn: { $ref: '#/components/schemas/Rendered' }
        honorific_prefix: { $ref: '#/components/schemas/Rendered' }
        given_name: { $ref: '#/components/schemas/Rendered' }
        additional_name: { $ref: '#/components/schemas/Rendered' }
        family_name: { $ref: '#/components/schemas/Rendered' }
        honorific_suffix: { $ref: '#/components/schemas/Rendered' }
        job_title: { $ref: '#/components/schemas/Rendered' }
        org:
          type: object
          properties:
            organization_name: { $ref: '#/components/schemas/Rendered' }
            organization_unit: { $ref: '#/components/schemas/Rendered' }
        contact:
          type: object
          properties:
            given_name: { $ref: '#/components/schemas/Rendered' }
            family_name: { $ref: '#/components/schemas/Rendered' }
        adr: { type: array, items: { $ref: '#/components/schemas/Address' } }
        tel: { type: array, items: { type: object }, description: Telephone numbers attached to an entry. }
        email: { type: array, items: { type: object }, description: Email addresses attached to an entry. }
        social: { type: array, items: { type: object }, description: Social networks attached to an entry. }
        bio: { $ref: '#/components/schemas/Rendered' }
        excerpt: { $ref: '#/components/schemas/Rendered' }
        notes: { $ref: '#/components/schemas/Rendered' }
        images: { type: object, description: Images attached to an entry. }
        visibility: { type: string, enum: [public, private, unlisted] }
        status: { type: string, enum: [approved, pending] }
    EntryWrite:
      type: object
      description: Write payload for an entry, transcribed from the route index argument schema.
      properties:
        type: { type: string, enum: [family, individual, organization] }
        slug: { type: string }
        fn: { type: object }
        honorific_prefix: { type: object }
        given_name: { type: object }
        additional_name: { type: object }
        family_name: { type: object }
        honorific_suffix: { type: object }
        job_title: { type: object }
        org: { type: object }
        contact: { type: object }
        adr: { type: array, items: { type: object } }
        tel: { type: array, items: { type: object } }
        email: { type: array, items: { type: object } }
        social: { type: array, items: { type: object } }
        bio: { type: object }
        excerpt: { type: object }
        notes: { type: object }
        images: { type: object }
        visibility: { type: string, enum: [public, private, unlisted] }
        status: { type: string, enum: [approved, pending] }
    Category:
      type: object
      description: A state, territory or grouping term the directory is organised by.
      properties:
        id: { type: integer }
        count: { type: integer }
        description: { type: string }
        link: { type: string, format: uri }
        name: { type: string }
        slug: { type: string }
        taxonomy: { type: string }
        parent: { type: integer }