> Reverse Contact API documentation. This is the Markdown copy of https://docs.reversecontact.com/use-cases/keep-profiles-fresh
> Every page is listed at https://docs.reversecontact.com/llms.txt, every endpoint with its credit cost and status at https://docs.reversecontact.com/catalog.json

# Keep profiles fresh

> Check person and company profile freshness for free, compare the last update date, and refresh stale data with live API jobs and polling.

- Last verified: 2026-11-04

Use your stored person or company profile URLs to check when Reverse Contact last fetched each profile. The status request is free and returns immediately. Refresh only stale or missing profiles with a live job, then retrieve the fresh data when the job ends.

## Which endpoint

| Input you have | Endpoint | Cost | Mode |
| --- | --- | --- | --- |
| Person URL or id to check | [Person profile status](https://docs.reversecontact.com/endpoints/check-person) | Free. Your workspace needs at least 2 credits | Synchronous |
| Company URL or id to check | [Company profile status](https://docs.reversecontact.com/endpoints/check-company) | Free. Your workspace needs at least 2 credits | Synchronous |
| Stale or missing person profile | [Person profile live](https://docs.reversecontact.com/endpoints/fetch-profile-live) | 2 credits when the job is created | Asynchronous |
| Stale or missing company profile | [Company profile live](https://docs.reversecontact.com/endpoints/fetch-company-live) | 2 credits when the job is created | Asynchronous |

## How it works

Set `RC_API_KEY` to your [API key](https://docs.reversecontact.com/authentication) and install the [TypeScript SDK](https://docs.reversecontact.com/sdk/javascript), [Python SDK](https://docs.reversecontact.com/sdk/python) or [CLI](https://docs.reversecontact.com/guides/cli). The shell examples use `jq`. You can adjust the examples' 30-day freshness threshold to match your own policy.

### Read the last update date for free

Read `data.exists` and `data.lastUpdate` from each status response. The `lastUpdate` field is an ISO 8601 timestamp, or `null` when the profile is absent. Keep these responses to skip live calls for fresh profiles in the next steps.

**Code Examples**

```bash [cURL]
curl --fail-with-body -sS -X POST https://api.reversecontact.com/v2/fetch/persons/check \
  -H "Authorization: Bearer $RC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.social.com/in/janedoe"}' > person-status.json
curl --fail-with-body -sS -X POST https://api.reversecontact.com/v2/fetch/companies/check \
  -H "Authorization: Bearer $RC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.social.com/company/acme-corp"}' > company-status.json
jq .data person-status.json company-status.json
```
```typescript [TypeScript SDK]
import ReverseContact from "@reverse-contact/sdk";

const client = new ReverseContact({ apiKey: process.env.RC_API_KEY });

const personStatus = await client.fetch.checkPerson({ url: "https://www.social.com/in/janedoe" });
const companyStatus = await client.fetch.checkCompany({ url: "https://www.social.com/company/acme-corp" });
console.log(personStatus.data, companyStatus.data);
```
```python [Python SDK]
import os

from reversecontact import ReverseContact

with ReverseContact(api_key=os.environ["RC_API_KEY"]) as client:
    person_status = client.fetch.check_person(url="https://www.social.com/in/janedoe")
    company_status = client.fetch.check_company(url="https://www.social.com/company/acme-corp")
    print(person_status.data, company_status.data)
```
```bash [CLI]
rc check person --url https://www.social.com/in/janedoe --json > person-status.json
rc check company --url https://www.social.com/company/acme-corp --json > company-status.json
jq .data person-status.json company-status.json
```

### Refresh the person only when needed

If the person is absent or `lastUpdate` is older than your threshold, start a live refresh and save the job id before waiting. Read the fresh person from `data.result` after the job succeeds.

**Code Examples**

```bash [cURL]
if jq -e '.data | .exists == true and (.lastUpdate != null) and
  ((.lastUpdate | sub("\\.[0-9]+Z$"; "Z") | fromdateiso8601) >= (now - 30 * 86400))' person-status.json > /dev/null; then
  echo "Cached profile is fresh; no live request"
else
  curl --fail-with-body -sS -X POST https://api.reversecontact.com/v2/fetch/persons/live \
    -H "Authorization: Bearer $RC_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"url": "https://www.social.com/in/janedoe"}' > person-job.json
  WEBHOOK_ID=$(jq -er .data.webhookId person-job.json)
  printf 'Save this job id: %s\n' "$WEBHOOK_ID"
  while :; do
    result=$(curl --fail-with-body -sS --max-time 30 \
      "https://api.reversecontact.com/v2/webhooks/$WEBHOOK_ID?wait=20" \
      -H "Authorization: Bearer $RC_API_KEY") || break
    case "$(printf '%s' "$result" | jq -r '.data.status')" in
      succeeded) printf '%s' "$result" | jq '.data.result'; break ;;
      errored) printf '%s' "$result" | jq '.data | {errorCode, creditsConsumed}'; break ;;
      created|in_progress) sleep 2 ;;
      *) printf '%s\n' "$result"; break ;;
    esac
  done
fi
```
```typescript [TypeScript SDK]
const personCutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
const personData = personStatus.data;
if (!personData.exists || !personData.lastUpdate || Date.parse(personData.lastUpdate) < personCutoff) {
  const job = await client.fetch.personLive({ url: "https://www.social.com/in/janedoe" });
  console.log("Save this job id:", job.id);
  const done = await job.wait();
  console.log(done.data.result);
} else {
  console.log("Cached profile is fresh; no live request");
}
```
```python [Python SDK]
from datetime import datetime, timedelta, timezone

person_cutoff = datetime.now(timezone.utc) - timedelta(days=30)
person_data = person_status.data
if person_data is None:
    raise RuntimeError("Status response is missing data")
person_updated = datetime.fromisoformat(str(person_data.last_update).replace("Z", "+00:00")) if person_data.last_update else None
if not person_data.exists or person_updated is None or person_updated < person_cutoff:
    with ReverseContact(api_key=os.environ["RC_API_KEY"]) as client:
        job = client.fetch.person_live(url="https://www.social.com/in/janedoe")
        print("Save this job id:", job.id)
        done = job.wait()
        print(done.data.result if done.data else None)
else:
    print("Cached profile is fresh; no live request")
```
```bash [CLI]
if jq -e '.data | .exists == true and (.lastUpdate != null) and
  ((.lastUpdate | sub("\\.[0-9]+Z$"; "Z") | fromdateiso8601) >= (now - 30 * 86400))' person-status.json > /dev/null; then
  echo "Cached profile is fresh; no live request"
else
  rc fetch person --url https://www.social.com/in/janedoe --live --wait --timeout 300
fi
```

### Refresh the company only when needed

If the company is absent or `lastUpdate` is older than your threshold, start a live refresh and save the job id before waiting. Read the fresh company from `data.result` after the job succeeds.

**Code Examples**

```bash [cURL]
if jq -e '.data | .exists == true and (.lastUpdate != null) and
  ((.lastUpdate | sub("\\.[0-9]+Z$"; "Z") | fromdateiso8601) >= (now - 30 * 86400))' company-status.json > /dev/null; then
  echo "Cached profile is fresh; no live request"
else
  curl --fail-with-body -sS -X POST https://api.reversecontact.com/v2/fetch/companies/live \
    -H "Authorization: Bearer $RC_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"url": "https://www.social.com/company/acme-corp"}' > company-job.json
  WEBHOOK_ID=$(jq -er .data.webhookId company-job.json)
  printf 'Save this job id: %s\n' "$WEBHOOK_ID"
  while :; do
    result=$(curl --fail-with-body -sS --max-time 30 \
      "https://api.reversecontact.com/v2/webhooks/$WEBHOOK_ID?wait=20" \
      -H "Authorization: Bearer $RC_API_KEY") || break
    case "$(printf '%s' "$result" | jq -r '.data.status')" in
      succeeded) printf '%s' "$result" | jq '.data.result'; break ;;
      errored) printf '%s' "$result" | jq '.data | {errorCode, creditsConsumed}'; break ;;
      created|in_progress) sleep 2 ;;
      *) printf '%s\n' "$result"; break ;;
    esac
  done
fi
```
```typescript [TypeScript SDK]
const companyCutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
const companyData = companyStatus.data;
if (!companyData.exists || !companyData.lastUpdate || Date.parse(companyData.lastUpdate) < companyCutoff) {
  const job = await client.fetch.companyLive({ url: "https://www.social.com/company/acme-corp" });
  console.log("Save this job id:", job.id);
  const done = await job.wait();
  console.log(done.data.result);
} else {
  console.log("Cached profile is fresh; no live request");
}
```
```python [Python SDK]
from datetime import datetime, timedelta, timezone

company_cutoff = datetime.now(timezone.utc) - timedelta(days=30)
company_data = company_status.data
if company_data is None:
    raise RuntimeError("Status response is missing data")
company_updated = datetime.fromisoformat(str(company_data.last_update).replace("Z", "+00:00")) if company_data.last_update else None
if not company_data.exists or company_updated is None or company_updated < company_cutoff:
    with ReverseContact(api_key=os.environ["RC_API_KEY"]) as client:
        job = client.fetch.company_live(url="https://www.social.com/company/acme-corp")
        print("Save this job id:", job.id)
        done = job.wait()
        print(done.data.result if done.data else None)
else:
    print("Cached profile is fresh; no live request")
```
```bash [CLI]
if jq -e '.data | .exists == true and (.lastUpdate != null) and
  ((.lastUpdate | sub("\\.[0-9]+Z$"; "Z") | fromdateiso8601) >= (now - 30 * 86400))' company-status.json > /dev/null; then
  echo "Cached profile is fresh; no live request"
else
  rc fetch company --url https://www.social.com/company/acme-corp --live --wait --timeout 300
fi
```

## What it costs

Person profile status and Company profile status cost **0 credits**, but each requires at least 2 credits in the workspace. Below that balance, the API returns `402 NO_CREDITS`. Status calls count toward your request rate limits.

Each live refresh consumes **2 credits when the job is created**. A `person-not-found` or `company-not-found` failure remains billed. Other failures can be refunded under the rules on the live endpoint pages. Polling costs 0 credits and does not count toward the per-minute rate limit.

> [!NOTE]
> Check 1,000 profiles for free. If 800 are fresh and you refresh the other 200, the live jobs consume 200 × 2 = **400 credits** at creation, before any failure refunds. This example reuses the profiles you already stored. Fetching fresh cached copies would add 1 credit per profile found.

## Good to know

Status reports the age of Reverse Contact's cache. If your stored copy needs updating but the cache is recent, use [Person profile](https://docs.reversecontact.com/endpoints/fetch-profile) or [Company profile](https://docs.reversecontact.com/endpoints/fetch-company) for 1 credit when found instead of running Live. See [Data freshness](https://docs.reversecontact.com/guides/data-freshness) for when to check, fetch or refresh.

Keep live jobs in the background, as results can take tens of minutes. The SDK raises `JobFailedError` on failure and `JobTimeoutError` when the wait deadline passes. After a timeout, resume with the saved id using `client.jobs.wait(jobId)` or `rc task`. A timeout alone does not require a new paid refresh. [Polling](https://docs.reversecontact.com/guides/polling) is recommended, and [webhooks](https://docs.reversecontact.com/guides/webhooks) are optional.

## FAQ

### How do I check when a person or company profile was last updated?

Call Person profile status or Company profile status with the profile URL or id. The free response includes `data.exists` and `data.lastUpdate`, the timestamp of the last fetch from the source.

### Why does a free profile status check return 402 NO_CREDITS?

Person profile status and Company profile status require at least 2 credits in the workspace even though the checks consume 0 credits. Below that balance, the API returns `402 NO_CREDITS`.

### How do I refresh only stale profiles with the API?

Compare `data.lastUpdate` against your own freshness threshold. Start Person profile live or Company profile live only when the cache is stale or missing, then retrieve `data.result` by polling.

### How much does refreshing 200 profiles cost?

Starting 200 live profile jobs consumes 400 credits at creation, before any failure refunds. Profile status checks and polling are free.

### Does a fresh status response update the profile stored in my CRM?

No. Profile status reports the age of Reverse Contact's cache. Retrieve that cached profile with Person profile or Company profile if your CRM copy needs updating. A cached profile costs 1 credit when found.
