UpHunt API

Error Reference

HTTP status codes, error shapes, and recommended responses.

All errors return a JSON body with an error field describing what went wrong. Some errors include additional context (e.g. creditsRemaining, needsCredits).

Status codes

CodeMeaningWhat to do
400Bad RequestCheck required fields (jobId, coverLetter). Verify field format.
401UnauthorizedAPI key is missing or invalid. Check your x-api-key header.
402Insufficient CreditsYou don't have enough auto-apply credits. Top up in the dashboard.
404Not FoundJob not found. Verify the jobId is correct and the job has been scraped.
409ConflictAlready applied to this job or application is already processing.
422Unprocessable EntityRequest was well-formed but semantically invalid (e.g. unknown timeline value).
429Rate LimitedToo many requests. See the Retry-After header and back off.
500Server ErrorSomething went wrong on our end. Retry after a brief delay.
503Service UnavailableUpwork itself is down or rate-limiting us. Retry after a minute.

Error shape

{
  "error": "Insufficient auto-apply credits",
  "creditsRemaining": 0,
  "needsCredits": true
}

Retryable vs. terminal

Retryable: 429, 500, 503. Use exponential backoff with jitter.

Terminal: 400, 401, 402, 404, 409, 422. Retrying without changing the request will hit the same error.

Retry helper

Node.js — retry with exponential backoff
async function callWithRetry<T>(fn: () => Promise<Response>, maxAttempts = 4): Promise<T> {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    const res = await fn()
    if (res.ok) return res.json()
 
    const retryable = res.status === 429 || res.status >= 500
    if (!retryable || attempt === maxAttempts) {
      throw Object.assign(new Error(`HTTP ${res.status}`), { status: res.status })
    }
 
    const retryAfter = Number(res.headers.get('retry-after')) || 0
    const backoff = Math.max(retryAfter * 1000, 2 ** attempt * 500 + Math.random() * 500)
    await new Promise(r => setTimeout(r, backoff))
  }
  throw new Error('unreachable')
}

Mapping applicationStatus failures

The status endpoint returns business-level failure reasons even when the HTTP response is 200. Most production integrations treat these as errors too:

StatusMeaning
failedGeneric failure — inspect errorMessage for details.
not_eligibleUpwork profile does not meet the job requirements. Not retryable.
not_enough_connectsTop up Connects on Upwork, then retry from scratch.
logged_outTransient — UpHunt reconnects the business developer automatically. Retry after a short delay.
not_availableJob was removed. Not retryable.

On this page