Developer API

Screen a habitational book in 10 minutes

A worked example for insurance underwriting and portfolio triage: resolve a submission to a Common Elements association record, pull its risk signals, and turn that into a triage decision. Two API calls, chained.

Overview

Say you're triaging an inbound habitational submission for a Florida condo association. You have a name and a state — that's enough to start. The flow is:

  1. GET /associations/lookup to resolve the submission to a Common Elements association ID.
  2. GET /associations/{id}/riskto pull that association's risk signals.
  3. Apply your own triage logic to the fields that come back.

Both calls count against your key's monthly request quota (250/month on Free, scaling up by tier — see plans). Screening a real book at volume is what the paid tiers and the commercial-use license are for. If you haven't generated a key yet, see Getting started.

1. Look up the association

The submission names "Pelican Bay Community Association" in Florida. Search by name and state:

curl "https://commonelements.com/api/v1/associations/lookup?q=Pelican+Bay&state=FL" \
  -H "Authorization: Bearer ce_live_your_key_here"

Response:

{
  "ok": true,
  "data": [
    {
      "id": "org_01JZA...",
      "name": "Pelican Bay Community Association",
      "state": "FL",
      "subtype": "condo",
      "unit_count": 742,
      "address": "6300 Pelican Bay Blvd",
      "city": "Naples",
      "zip": "34108",
      "external_document_number": "N04000006125",
      "is_claimed": true,
      "created_at": "2025-11-02T14:00:00Z"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0
}

One match. Grab data[0].id— that's what you pass to the risk endpoint next. unit_countis also worth capturing now: a per-unit exposure estimate needs it, and the risk endpoint doesn't repeat every field from the lookup response.

If total comes back 0or greater than 1, you don't have a clean match — fall back to a narrower query (add registration_number if the submission includes a DBPR or state filing number) or flag the account for manual matching rather than guessing.

2. Pull its risk signals

Now call the risk endpoint with the ID from step 1:

curl "https://commonelements.com/api/v1/associations/org_01JZA.../risk" \
  -H "Authorization: Bearer ce_live_your_key_here"

Response:

{
  "ok": true,
  "data": {
    "organization_id": "org_01JZA...",
    "name": "Pelican Bay Community Association",
    "state": "FL",
    "unit_count": 742,
    "county": "Collier",
    "county_fips": "12021",
    "composite_risk_score": 68.4,
    "composite_risk_rating": "Relatively High",
    "expected_annual_loss_score": 71.2,
    "expected_annual_loss_rating": "Relatively High",
    "social_vulnerability_score": 42.1,
    "social_vulnerability_rating": "Relatively Moderate",
    "source": "FEMA National Risk Index (county level)",
    "has_data": true
  }
}

3. Interpret the fields

Everything under datahere comes from FEMA's National Risk Index, joined to the association's county. It's a county-level catastrophe and community-resilience signal, not a parcel- or building-specific score:

FieldTypeUnderwriting read
composite_risk_score / composite_risk_ratingnumber | null, string | nullFEMA's overall natural-hazard risk for the county, combining expected loss, social vulnerability, and community resilience. A high rating on a submission is a reason to look closer, not an automatic decline.
expected_annual_loss_score / expected_annual_loss_ratingnumber | null, string | nullHow much of the composite risk is driven by expected dollar losses from hazards specifically. Weighs more heavily than social vulnerability if you're pricing cat exposure rather than assessing claims-handling friction.
social_vulnerability_score / social_vulnerability_ratingnumber | null, string | nullA community's capacity to prepare for, respond to, and recover from a hazard event. Relevant to claims severity and time-to-recovery after a catastrophe, not to the likelihood of a hazard occurring.
county / county_fipsstring | nullWhich county the score applies to — useful for portfolio-level rollups (concentration by county) and for sanity-checking the match.
has_databooleanFalse when the association's county hasn't been matched to a FEMA NRI record yet. Treat false as 'no signal,' not 'low risk' — route it to manual underwriting rather than assuming safety.
sourcestringAlways attributed. Cite this on the underwriting file so the decision is defensible.

A simple triage rule from these fields: auto-route to standard review when has_data is true and both composite_risk_rating and expected_annual_loss_ratingare below "Relatively High," escalate to a senior underwriter when either is "Relatively High" or above, and route to manual review whenever has_data is false.

Running it across a book

To screen more than one submission, loop the same two calls per row of your book. A minimal Node example:

const API_KEY = process.env.CE_API_KEY;
const BASE = "https://commonelements.com/api/v1";

async function screenSubmission(name, state) {
  const lookupRes = await fetch(
    `${BASE}/associations/lookup?q=${encodeURIComponent(name)}&state=${state}`,
    { headers: { Authorization: `Bearer ${API_KEY}` } },
  );
  const lookup = await lookupRes.json();

  if (!lookup.ok || lookup.total !== 1) {
    return { name, state, status: "needs_manual_match" };
  }

  const assocId = lookup.data[0].id;
  const riskRes = await fetch(`${BASE}/associations/${assocId}/risk`, {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });
  const risk = await riskRes.json();

  if (!risk.ok || !risk.data.has_data) {
    return { name, state, assocId, status: "no_risk_data" };
  }

  const highRisk =
    risk.data.composite_risk_rating?.startsWith("Relatively High") ||
    risk.data.expected_annual_loss_rating?.startsWith("Relatively High");

  return {
    name,
    state,
    assocId,
    county: risk.data.county,
    compositeRiskRating: risk.data.composite_risk_rating,
    status: highRisk ? "escalate" : "standard_review",
  };
}

// screenSubmission("Pelican Bay Community Association", "FL").then(console.log);

For a real book, batch this behind your own concurrency limit and respect the rate limit for your tier (see the rate limits section of the reference). The X-RateLimit-Remaining response header tells you how much headroom you have left in the current window before you need to slow down.

What this endpoint does not cover yet

Parcel-level flood zone, SIRS status, and coverage fields are not in this response

The risk endpoint documented above returns county-level FEMA National Risk Index signals. It does not currently return a parcel-specific FEMA flood zone, Florida SIRS or milestone-inspection status, or workers'-compensation coverage details — even though structural-integrity and flood-exposure data are part of the broader Common Elements dataset described for the insurance use case. If your underwriting workflow needs those specific fields at the API layer, check the full API reference for the latest endpoints, or talk to the data team about an enterprise data agreement.

Ready to screen your own book?

Generate an API key and adapt the script above to your submission data.

Get your API key