Layers
Partner APIAPI referenceKeywords

POST /v1/projects/:projectId/keywords/refresh

Kick off a refresh of the project's curated TikTok hashtag bank.

View as Markdown
POST/v1/projects/:projectId/keywords/refresh
stableidempotent
Auth
Bearer
Scope
projects:write

Kicks off Layers' keyword research agent against the project's current appDescription and upserts the result into the project's keyword bank when it lands. The route is async — it returns a job envelope immediately; the agent runs in the background for 4 – 5 minutes end-to-end.

Partners typically fire this fire-and-forget and poll GET /v1/projects/:projectId/keywords until refreshedAt advances (or hashtags.length > 0 on first refresh). The job envelope is there if you want stage-level visibility.

When to call

Most partners never need to call this endpoint directly — Layers auto-triggers keyword research from POST /v1/projects (when appDescription is supplied) and from PATCH /v1/projects/:id (when appDescription changes). The auto-trigger is fire-and-forget; observe the result via GET /v1/projects/:projectId/keywords.

Reach for this endpoint when you want:

  • A jobId for stage-level observability. The auto-trigger doesn't surface a job envelope; this endpoint does.
  • A manual re-run if the auto-trigger failed (rare — GET /keywords returns refreshedAt: null and an empty bank when it never landed).
  • A forced refresh that doesn't depend on appDescription changing.

Send an Idempotency-Key header so a retry on connection error replays the cached job envelope instead of starting a second run.

Precondition

projects.app_description must be set. Without it, the route returns 422 VALIDATION with missingFields: ["appDescription"]. Set it on the project first via POST /v1/projects or PATCH /v1/projects/:id.

Body

Empty body. The handler reads appDescription from the project row.

curl -X POST https://api.layers.com/v1/projects/$PROJECT_ID/keywords/refresh \
  -H "Authorization: Bearer $LAYERS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'

Response

202Job accepted. Poll /v1/jobs/:jobId for terminal state, or poll /v1/projects/:id/keywords for the bank.
{
  "jobId": "job_01HZX...",
  "kind": "project_keywords_refresh",
  "status": "running",
  "projectId": "prj_01HX...",
  "startedAt": "2026-05-11T19:02:11.959Z",
  "locationUrl": "/v1/jobs/job_01HZX..."
}

Errors

StatusCodeWhen
422VALIDATIONProject has no appDescription. Response carries missingFields + a remedy hint.
401UNAUTHENTICATEDMissing or invalid key.
403FORBIDDEN_SCOPEKey lacks projects:write.
404NOT_FOUNDProject not in this org.

Job-level failures (the agent failed mid-run) surface on GET /v1/jobs/:jobId as status: "failed" with a structured error. Safe to retry with the same Idempotency-Key.

Notes

  • Expected end-to-end latency is 4 – 5 minutes. The agent runs an iterative keyword-expansion + scoring loop and consults third-party TikTok data for popularity signals.
  • The agent filters aggressively — banks of 0 hashtags happen when the project's appDescription is too vague or off-domain. Tighten the description and call refresh again.
  • Curated hashtags meet score, view-count, and post-count floors. Anything below is dropped before the bank lands.

See also

On this page