API Reference

The Hadef AI public API lets you run the research pipeline, retrieve pitches, and record the sends you make yourself. Hadef sends nothing. All endpoints are under /v1/ and require an API key, which is available on every plan. One credit is spent per completed run; every other call is free.

Base URL: https://hadef.io  ·  All endpoints accept and return JSON.

Authentication

Create API keys from Settings → API keys. Pass the key as a Bearer token:

Authorization: Bearer hdk_your_api_key_here

Keys are user-scoped. A revoked key is rejected immediately. The full key is shown once at creation, so store it securely.

Rate limits

Requests are rate limited per API key. A request over the limit returns 429 Too Many Requests with a Retry-After header giving the number of seconds to wait. The JSON body carries "code": "rate_limited" and a hint that states the limit in force.

Error format

All errors return JSON with an error field:

{"error": "Invalid or missing API key", "hint": "Authorization: Bearer hdk_..."}

Pipeline

POST /v1/pipeline/run

Kick off a research pipeline run for a prospect. Returns immediately with a prospect_id to poll.

Request body

FieldTypeDescription
linkedin_url requiredstringFull LinkedIn profile URL
prospect_namestringFull name (optional, improves research accuracy)
curl -X POST https://hadef.io/v1/pipeline/run \
  -H "Authorization: Bearer hdk_..." \
  -H "Content-Type: application/json" \
  -d '{"linkedin_url":"https://linkedin.com/in/example","prospect_name":"Jane Smith"}'

Response

{"prospect_id": "uuid", "status": "queued", "job_id": "uuid", "linkedin_url": "..."}
GET /v1/pipeline/{prospect_id}

Poll a run until status is no longer researching. A run ends as pitched (a pitch was written and passed QA), researched (a pitch was written and did not pass QA), disqualified (not enough public material, no pitch) or new (no verdict, no pitch; you can run it again). The pitch field is the latest pitch for the prospect, and pitch.status is qa_passed, qa_failed, or sent once you have recorded a send. Wait on status, not on the presence of pitch: when you research a guest again, the pitch from the earlier run is still returned while the new run is in progress.

curl https://hadef.io/v1/pipeline/uuid \
  -H "Authorization: Bearer hdk_..."

Response

{
  "prospect_id": "uuid",
  "status": "pitched",
  "updated_at": "2026-04-26T09:00:00Z",
  "pitch": {
    "pitch_id": "uuid",
    "status": "qa_passed",
    "linkedin_message": "...",
    "email_subject": "...",
    "email_body": "..."
  }
}

Pitch

POST /v1/pitch/mark-sent

Record that you sent a pitch yourself, and on which channel. It only records the send: Hadef delivers nothing, and the call is free.

FieldTypeDescription
pitch_id requiredstringUUID from pipeline response
channelstringlinkedin (default) or email
curl -X POST https://hadef.io/v1/pitch/mark-sent \
  -H "Authorization: Bearer hdk_..." \
  -H "Content-Type: application/json" \
  -d '{"pitch_id":"uuid","channel":"linkedin"}'

Response

{"sent_at": "2026-04-26T09:01:00Z", "channel": "linkedin", "already_marked": false, "delivered_by_hadef": false}

Prospects

GET /v1/prospects

List your prospects. Paginated, newest first.

Query paramDefaultDescription
limit20Max 100
offset0Pagination offset
curl "https://hadef.io/v1/prospects?limit=10&offset=0" \
  -H "Authorization: Bearer hdk_..."

Credits

GET /v1/credits

Check remaining credits and plan tier.

curl https://hadef.io/v1/credits \
  -H "Authorization: Bearer hdk_..."

Response

{"remaining": 47, "plan": "professional"}

Zapier integration

Use the Webhooks by Zapier action to call Hadef from any Zap:

  1. Add a Webhooks by Zapier step, choose POST
  2. URL: https://hadef.io/v1/pipeline/run
  3. Headers: Authorization: Bearer hdk_your_key
  4. Data: {"linkedin_url": "{{LinkedIn URL field}}"}
  5. Add a second step to poll /v1/pipeline/{{prospect_id}} until status is no longer researching, then read pitch if it ended as pitched or researched

curl examples

Full pipeline in bash

KEY="hdk_your_key_here"

# Start pipeline
PROSPECT=$(curl -s -X POST https://hadef.io/v1/pipeline/run \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"linkedin_url":"https://linkedin.com/in/example"}')

ID=$(echo $PROSPECT | python3 -c "import sys,json; print(json.load(sys.stdin)['prospect_id'])")
echo "Pipeline started: $ID"

# Poll until done
while true; do
  STATUS=$(curl -s https://hadef.io/v1/pipeline/$ID \
    -H "Authorization: Bearer $KEY")
  S=$(echo $STATUS | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
  echo "Status: $S"
  [ "$S" != "researching" ] && break
  sleep 30
done

# pitched or researched: this run wrote a pitch. Anything else: it did not.
case "$S" in pitched|researched) ;; *) echo "Run ended without a pitch: $S"; exit 1;; esac

# Print pitch
echo $STATUS | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['pitch']['linkedin_message'])"

Hadef API v1 · Manage keys