Aerin Medical Site API (WordPress REST)

Anonymously readable REST surface served by aerinmedical.com at /wp-json — 321 routes across 15 namespaces. The substantive part is Aerin Medical's own doctor-finder plugin (em-locator/v1): 1,012 ENT practice locations trained on VivAer or RhinAer, each with name, formatted address, phone, latitude/longitude and a public permalink, filterable by free text, by lat/lng proximity, by product (RhinAer 783 locations, VivAer 920) and by Center of Excellence designation (60 locations). Site search (wp/v2/search, 2,242 items), oEmbed and the route-discovery documents also answer anonymously, while every standard wp/v2 content collection returns 401. This is an incidental site surface, not an API product: Aerin Medical publishes no documentation, keys, SDKs, support channel, rate-limit policy, status page or terms of use for it.

Documentation

Specifications

Other Resources

OpenAPI Specification

aerin-medical-site-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: Aerin Medical Site API (WordPress REST)
  version: em-locator/v1 + aerin + wp/v2
  summary: Anonymously callable REST surface served by aerinmedical.com — the ENT doctor
    locator (1,012 treating locations), site search, and the site's form-submission endpoints.
  description: 'Aerin Medical is a medical-device company and operates no developer API
    product: it publishes no developer portal, no API documentation, no keys, no SDKs and
    no terms of use for programmatic access.


    Its corporate site, aerinmedical.com, does however serve a WordPress REST API at
    `https://aerinmedical.com/wp-json` with 321 registered routes across 15 namespaces. Most
    of the standard `wp/v2` content routes (posts, pages, media, users, taxonomies, types)
    are BLOCKED to anonymous callers by the iThemes Security plugin and return HTTP 401
    `itsec_rest_api_access_restricted` — so, unlike most WordPress sites, the content
    library here is not machine-readable. What IS anonymously readable is the company''s
    own first-party doctor-finder plugin (`em-locator/v1`, the surface behind
    https://aerinmedical.com/find-ent-doctor/), the cross-content `wp/v2/search` index, the
    route discovery documents, and a small set of `aerin`-namespace POST endpoints that
    back the site''s lead-capture and NOSE-score assessment forms.


    This document was DERIVED by API Evangelist from the provider''s own live route index
    (`GET https://aerinmedical.com/wp-json/`) plus direct anonymous probing of each route,
    on 2026-07-31. Every path, method, parameter name, enumeration and response field is
    taken from that index or from an observed HTTP 200 response; nothing was invented.
    Routes observed to return 401 anonymously are deliberately omitted. The `aerin`
    namespace POST routes are listed because they are really registered and really public,
    but they declare NO argument schema in the route index and they have side effects
    (they submit leads / contact requests / newsletter sign-ups into the company''s
    systems), so they were NOT invoked and no request body is documented for them.


    Treat this as an incidental site surface, not a supported product API. Aerin Medical
    offers no availability, versioning or support commitment for it.'
  contact:
    name: Aerin Medical, Inc.
    url: https://aerinmedical.com/contact-us/
    email: customerservice@aerinmedical.com
  x-origin:
  - url: https://aerinmedical.com/wp-json/
    format: wordpress-rest-route-index
    version: wp/v2
  x-apievangelist-method: derived
  x-apievangelist-derived-from: https://aerinmedical.com/wp-json/ (live route index + per-route
    anonymous probes, fetched 2026-07-31)
  x-apievangelist-note: Incidental site API, not a product API. Derived by API Evangelist,
    not published by Aerin Medical.
servers:
- url: https://aerinmedical.com/wp-json
  description: aerinmedical.com WordPress REST API
tags:
- name: Discovery
  description: Route index / discovery documents.
- name: Locator
  description: The Aerin doctor finder — treating ENT locations, filterable by product,
    designation, text and geography.
- name: Search
  description: Cross-content-type site search.
- name: Forms
  description: Site form-submission endpoints (lead capture, NOSE score assessments).
    Side-effecting; not invoked during derivation.
- name: Embed
  description: oEmbed representations of aerinmedical.com URLs.
paths:
  /:
    get:
      tags: [Discovery]
      operationId: getRouteIndex
      summary: Get the WordPress REST route index
      description: Returns the site's discovery document — name, home URL, the 15 registered
        namespaces, all 321 routes with their methods and declared arguments, and the
        supported authentication method (WordPress application passwords).
      responses:
        '200':
          description: The route index.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouteIndex'
  /aerin:
    get:
      tags: [Discovery]
      operationId: getAerinNamespaceIndex
      summary: Get the aerin namespace index
      description: Returns the route index scoped to Aerin Medical's own `aerin` namespace —
        the site's seven form-handling POST routes.
      parameters:
      - name: namespace
        in: query
        required: false
        schema: {type: string, default: aerin}
      - name: context
        in: query
        required: false
        schema: {type: string, default: view}
      responses:
        '200':
          description: The namespace index.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouteIndex'
  /em-locator/v1:
    get:
      tags: [Discovery]
      operationId: getLocatorNamespaceIndex
      summary: Get the em-locator namespace index
      responses:
        '200':
          description: The namespace index.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouteIndex'
  /em-locator/v1/locations:
    get:
      tags: [Locator]
      operationId: listTreatingLocations
      summary: List ENT locations trained on Aerin procedures
      description: 'Returns the treating locations behind https://aerinmedical.com/find-ent-doctor/ —
        1,012 records as observed on 2026-07-31. Each record carries the practice name,
        formatted address, phone, latitude/longitude, the public location permalink, and
        pre-rendered HTML fragments used by the site''s map and result list.


        Pagination is standard WordPress: 10 records per page by default, with the total
        count in the `X-WP-Total` response header and the page count in `X-WP-TotalPages`.
        Supplying `lat` and `lng` switches the collection into proximity mode — results are
        ordered by distance and each record''s `distance` field becomes a number (miles)
        instead of `false`.


        The route index declares no arguments for this route; every parameter below was
        confirmed by observed behaviour (differing `X-WP-Total` / ordering / `distance`
        values) against the live endpoint. `product` and `designation` take the WordPress
        term IDs exposed by the locator form at `GET /em-locator/v1/locator`.


        CORS is open (`Access-Control-Allow-Origin: *`) and responses are cached
        (`Cache-Control: max-age=600, must-revalidate`).'
      parameters:
      - name: page
        in: query
        description: Page of the collection. Observed 102 pages at 10 per page.
        required: false
        schema: {type: integer, minimum: 1, default: 1}
      - name: per_page
        in: query
        description: Records per page.
        required: false
        schema: {type: integer, default: 10}
      - name: search
        in: query
        description: Free-text filter over practice name / city / state. Observed to narrow
          `X-WP-Total` (e.g. `search=Austin` → 44).
        required: false
        schema: {type: string}
      - name: lat
        in: query
        description: Latitude of the search origin. Supply together with `lng` to sort by
          proximity and populate `distance`.
        required: false
        schema: {type: number, format: double}
      - name: lng
        in: query
        description: Longitude of the search origin. Supply together with `lat`.
        required: false
        schema: {type: number, format: double}
      - name: product
        in: query
        description: Filter to locations offering one product, by term ID. Observed values —
          `697` RhinAer (783 locations), `698` VivAer (920 locations).
        required: false
        schema:
          type: string
          enum: ['697', '698']
          x-enum-descriptions: {'697': RhinAer, '698': VivAer}
      - name: designation
        in: query
        description: Filter to Aerin Center of Excellence designations, by term ID or the
          `center-of-excellence` roll-up. Observed counts — any Center of Excellence 60,
          `699` Premier VivAer 17, `700` Advanced RhinAer 22, `701` Advanced VivAer 30,
          `702` Premier RhinAer 5.
        required: false
        schema:
          type: string
          enum: [center-of-excellence, '699', '700', '701', '702']
          x-enum-descriptions:
            center-of-excellence: Any Center of Excellence
            '699': Premier VivAer
            '700': Advanced RhinAer
            '701': Advanced VivAer
            '702': Premier RhinAer
      responses:
        '200':
          description: 'A page of treating locations. `X-WP-Total` carries the full match count;
            an out-of-range page returns an empty array with `X-WP-Total: 0`.'
          headers:
            X-WP-Total:
              description: Total records matching the query.
              schema: {type: integer}
            X-WP-TotalPages:
              description: Total pages available.
              schema: {type: integer}
            Access-Control-Allow-Origin:
              description: Observed as `*`.
              schema: {type: string}
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Location'
  /em-locator/v1/locator:
    get:
      tags: [Locator]
      operationId: getLocatorWidget
      summary: Get the rendered doctor-finder widget
      description: Returns the server-rendered HTML for the doctor-finder search widget,
        including the product and designation filter `<select>` elements whose option values
        are the term IDs accepted by `listTreatingLocations`.
      responses:
        '200':
          description: The rendered widget.
          content:
            application/json:
              schema:
                type: object
                properties:
                  html:
                    type: string
                    description: Server-rendered HTML for the locator search form and map shell.
  /custom-footer/v1/get-custom-footer-html:
    get:
      tags: [Discovery]
      operationId: getCustomFooterHtml
      summary: Get the rendered site footer
      description: Returns the site's shared footer markup. Used by the theme; exposed
        anonymously.
      responses:
        '200':
          description: The rendered footer.
          content:
            application/json:
              schema:
                type: object
  /wp/v2/search:
    get:
      tags: [Search]
      operationId: searchSite
      summary: Search across aerinmedical.com content
      description: 'Cross-content-type search. 2,242 items were indexed as observed on
        2026-07-31. This is the ONLY `wp/v2` collection reachable anonymously — `posts`,
        `pages`, `media`, `categories`, `tags`, `users`, `comments`, `types`, `taxonomies`
        and `statuses` all return 401 `itsec_rest_api_access_restricted`, and the `_links.self`
        hrefs in each result therefore also 401. Results carry `id`, `title`, `url`, `type`
        and `subtype` only.


        All parameters below are declared verbatim in the site''s own route index.'
      parameters:
      - name: context
        in: query
        description: Scope under which the request is made; determines fields present in response.
        required: false
        schema: {type: string, enum: [view, embed], default: view}
      - name: page
        in: query
        description: Current page of the collection.
        required: false
        schema: {type: integer, minimum: 1, default: 1}
      - name: per_page
        in: query
        description: Maximum number of items to be returned in result set.
        required: false
        schema: {type: integer, minimum: 1, maximum: 100, default: 10}
      - name: search
        in: query
        description: Limit results to those matching a string.
        required: false
        schema: {type: string}
      - name: type
        in: query
        description: Limit results to items of an object type.
        required: false
        schema: {type: string, enum: [post, term, post-format], default: post}
      - name: subtype
        in: query
        description: 'Limit results to items of one or more object subtypes. Note the
          Aerin-specific custom post types: `location`, `physician`, `alert`, and the
          `em_designation` taxonomy.'
        required: false
        schema:
          type: array
          default: [any]
          items:
            type: string
            enum: [post, page, location, physician, alert, category, post_tag, em_designation, any]
      - name: exclude
        in: query
        description: Ensure result set excludes specific IDs.
        required: false
        schema: {type: array, default: [], items: {type: integer}}
      - name: include
        in: query
        description: Limit result set to specific IDs.
        required: false
        schema: {type: array, default: [], items: {type: integer}}
      responses:
        '200':
          description: A page of search results.
          headers:
            X-WP-Total:
              description: Total items matching the query.
              schema: {type: integer}
            X-WP-TotalPages:
              description: Total pages available.
              schema: {type: integer}
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SearchResult'
  /oembed/1.0/embed:
    get:
      tags: [Embed]
      operationId: getOEmbed
      summary: Get an oEmbed representation of an aerinmedical.com URL
      parameters:
      - name: url
        in: query
        required: true
        description: The URL of the resource for which to fetch oEmbed data.
        schema: {type: string, format: uri}
      - name: format
        in: query
        required: false
        schema: {type: string, enum: [json, xml], default: json}
      - name: maxwidth
        in: query
        required: false
        schema: {type: integer, default: 600}
      responses:
        '200':
          description: The oEmbed response.
          content:
            application/json:
              schema:
                type: object
  /aerin/locations:
    post:
      tags: [Forms]
      operationId: submitLocationsFormQuery
      summary: Submit a doctor-finder query from the site form
      description: Registered POST route backing the doctor-finder page. NOT INVOKED during
        derivation — the route index declares no arguments and the endpoint has side effects,
        so no request body is documented. Use `listTreatingLocations` for read access.
      x-apievangelist-undeclared-request-body: true
      responses:
        default:
          description: Not observed — endpoint was not invoked.
  /aerin/google:
    post:
      tags: [Forms]
      operationId: submitGoogleProxyRequest
      summary: Server-side proxy used by the locator's Google Maps integration
      description: Registered POST route in the `aerin` namespace. NOT INVOKED during
        derivation; no argument schema is declared or documented.
      x-apievangelist-undeclared-request-body: true
      responses:
        default:
          description: Not observed — endpoint was not invoked.
  /aerin/contact:
    post:
      tags: [Forms]
      operationId: submitContactForm
      summary: Submit the site contact form
      description: Registered POST route backing https://aerinmedical.com/contact-us/.
        Side-effecting (creates a contact record); NOT INVOKED during derivation and no
        request body is documented.
      x-apievangelist-undeclared-request-body: true
      responses:
        default:
          description: Not observed — endpoint was not invoked.
  /aerin/newsletter:
    post:
      tags: [Forms]
      operationId: submitNewsletterSignup
      summary: Submit a newsletter sign-up
      description: Registered POST route backing the site's e-mail sign-up. Side-effecting;
        NOT INVOKED during derivation and no request body is documented.
      x-apievangelist-undeclared-request-body: true
      responses:
        default:
          description: Not observed — endpoint was not invoked.
  /aerin/request-evaluation:
    post:
      tags: [Forms]
      operationId: submitEvaluationRequest
      summary: Submit a request to be contacted for an evaluation
      description: Registered POST route backing the site's patient evaluation request flow.
        Side-effecting (creates a patient lead); NOT INVOKED during derivation and no request
        body is documented.
      x-apievangelist-undeclared-request-body: true
      responses:
        default:
          description: Not observed — endpoint was not invoked.
  /aerin/nose-score:
    post:
      tags: [Forms]
      operationId: submitNoseScore
      summary: Submit a NOSE (Nasal Obstruction Symptom Evaluation) assessment
      description: Registered POST route backing the site's nasal-obstruction self-assessment.
        Side-effecting and potentially collects health-related answers; NOT INVOKED during
        derivation and no request body is documented.
      x-apievangelist-undeclared-request-body: true
      responses:
        default:
          description: Not observed — endpoint was not invoked.
  /aerin/rhinitis-nose-score:
    post:
      tags: [Forms]
      operationId: submitRhinitisNoseScore
      summary: Submit a chronic-rhinitis symptom self-assessment
      description: Registered POST route backing the site's chronic-rhinitis self-assessment.
        Side-effecting and potentially collects health-related answers; NOT INVOKED during
        derivation and no request body is documented.
      x-apievangelist-undeclared-request-body: true
      responses:
        default:
          description: Not observed — endpoint was not invoked.
components:
  securitySchemes:
    applicationPassword:
      type: http
      scheme: basic
      description: 'WordPress application passwords, advertised by the site''s own route index
        at `authentication.application-passwords.endpoints.authorization` =
        https://aerinmedical.com/wp-admin/authorize-application.php. This is the built-in
        WordPress mechanism for authenticated (administrative) calls; Aerin Medical issues no
        public credentials, and every operation in this document is reachable ANONYMOUSLY
        with no credential at all.'
  schemas:
    RouteIndex:
      type: object
      description: A WordPress REST route-discovery document.
      properties:
        name: {type: string}
        description: {type: string}
        url: {type: string, format: uri}
        home: {type: string, format: uri}
        namespaces:
          type: array
          items: {type: string}
        authentication: {type: object}
        routes:
          type: object
          description: Map of route pattern to its namespace, methods, endpoints and declared args.
        _links: {type: object}
    Location:
      type: object
      description: One ENT practice location trained on Aerin procedures.
      properties:
        ID:
          type: integer
          description: WordPress post ID of the location.
        address:
          type: string
          description: Street address as entered.
        formatted_address:
          type: string
          description: Geocoded, display-formatted address.
        lat:
          type: string
          description: Latitude, as a string.
        lng:
          type: string
          description: Longitude, as a string.
        distance:
          oneOf:
          - {type: boolean}
          - {type: number}
          description: '`false` unless `lat`/`lng` were supplied, in which case the distance
            from the search origin in the units given by `plugin_settings.distance_unit`.'
        phone:
          type: string
          description: Practice phone number, formatted for display.
        email:
          type: string
          description: Contact e-mail. Observed empty on every sampled record.
        website:
          type: string
          description: Practice website. Observed empty on every sampled record.
        permalink:
          type: string
          format: uri
          description: Public page for this location on aerinmedical.com.
        list_item_html:
          type: string
          description: Pre-rendered `<li>` fragment for the result list, including the product
            badges (RhinAer / VivAer) shown on the site.
        map_item_html:
          type: string
          description: Pre-rendered map marker fragment.
        map_details_html:
          type: string
          description: Pre-rendered map info-window fragment.
        plugin_settings:
          type: object
          description: 'Locator plugin configuration echoed on every record — map centre, zoom,
            distance unit, page size, labels. NOTE: as observed on 2026-07-31 this object also
            echoes the site''s Google Maps browser API key and empty basic-auth fields to every
            anonymous caller. Recorded as an observation only; the value is deliberately not
            reproduced in this repository. See security/aerin-medical-domain-security.yml.'
        post:
          type: object
          description: 'The raw underlying WordPress post record. NOTE: as observed on
            2026-07-31 its `post_content` is a JSON blob carrying internal CRM fields —
            Salesforce id, account number and the named sales representative for the account —
            alongside the public address and product flags. Recorded as an observation; values
            are deliberately not reproduced here.'
          properties:
            ID: {type: integer}
            post_title: {type: string}
            post_name: {type: string}
            post_type: {type: string, const: location}
            post_status: {type: string}
            post_date: {type: string}
            post_modified: {type: string}
            post_parent: {type: integer}
            post_content:
              type: string
              description: JSON string with address, city, state, zip, country and the
                `rhinaer` / `vivaer` / `inoffice` / `excellence` product flags — plus internal
                CRM fields (see note above).
    SearchResult:
      type: object
      properties:
        id: {type: integer}
        title: {type: string}
        url: {type: string, format: uri}
        type: {type: string}
        subtype: {type: string}
        _links:
          type: object
          description: Hypermedia links. The `self` href points at a `wp/v2` collection that
            returns 401 to anonymous callers.
    Error:
      type: object
      description: The WordPress REST error envelope.
      properties:
        code: {type: string}
        message: {type: string}
        data:
          type: object
          properties:
            status: {type: integer}