Skip to main content

Search Flow

This guide describes a face search from the client's side: which calls to make, in what order, what each response means, and where the credit is charged. For copy-paste curl commands, see the Quickstart.


Overview​

A search is asynchronous. The upload returns a searchId at once; the face search then runs in the background. The client waits for completed, shows the candidate matches, lets the user pick, and then reads the full profiles.


Step 0 — Check the account​

GET /users/me returns three fields that decide what the client may do next:

FieldMeaning
canSearchfalse → the user must buy a plan before starting a new search. Treat only an explicit false as blocking.
paymentStageselection or results — which step charges the credit. Missing or null means selection.
credits.availableCredits left to unlock results.

Step 1 — Upload​

POST /search/upload (multipart, field file) with a JPEG, PNG or HEIC photo.

ResponseMeaning
200 {searchId, status: "processing", detectedFaces}Search started. detectedFaces: 0 means no face was found.
402Search limit reached: a user without a subscription has used the free searches. Upload never spends a credit.
400No file, or the file is too large.

Step 2 — Wait for the result​

Poll GET /search/{searchId}/status every few seconds, or open GET /search/{searchId}/status/stream, a Server-Sent Events stream that pushes the same SearchStatusResponse object on every change.

statusMeaningNext
processingThe search is running.Keep waiting.
previewAn early partial result; thumbnail is set.Keep waiting for completed.
completedMatches are ready.Step 3.
errorThe search failed; see message.Stop.

Age restriction​

If the status response has showAgeRestrictionPopup: true, show the user the age restriction notice. After the user accepts, call POST /search/{searchId}/age-restriction-consent (204). The notice is not shown again for that search.


Step 3 — Matches​

GET /search/{searchId}/matches returns candidates grouped by social network:

  • groups[] — one group per network (source), each with the first page of matches and a nextCursor when there are more. Load the next page with GET /search/{searchId}/matches/{source}?cursor=<nextCursor>&limit=<n>.
  • totalCount — matches across all networks; 0 means nothing was found.
  • maxSelectable — the most matches the user may pick in step 4.

Match pictures are blurred and watermarked at this step. After selection this endpoint returns 400; use the results endpoint instead.


Step 4 — Select​

POST /search/{searchId}/select with {"matchIds": ["<id>", ...]} — the matches the user picked, at most maxSelectable. An empty array means "none of these". Selection can be made once per search.

ResponseMeaning
200Selection saved. With paymentStage = selection, one credit was spent.
402No credits available (paymentStage = selection only). The selection is rolled back; the user can buy credits and call select again.
400Already selected, or more than maxSelectable IDs.

Step 5 — Results​

GET /search/{searchId}/results returns consensusName and profiles[] (source, username, fullname, profilePicture, bio, profileUrl, isPrivate).

With paymentStage = results, the first call for a search spends the credit:

Credits availableAlready paid for this searchResponse
0no200, isBlurred: true — profile data is masked
0yes200, full results
> 0noOne credit is spent, 200, full results
> 0yes200, full results — never charged twice

This endpoint never returns 402. Calling it before step 4 returns 400.

Add ?getSocialprofiler=true to include the Social Profiler report once it is ready, or call GET /search/{searchId}/socialprofiler (SSE: /socialprofiler/stream).


CallPurpose
GET /search/historyPast searches, paginated with cursor and limit.
GET /search/{searchId}One search's details.
DELETE /search/{searchId}Remove a search from history.

People can ask to be removed from Face2Social through the public opt-out form (POST /optout). Removed profiles disappear from matches and results, including in searches made earlier.