> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fetchin.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Get Profile Skills

> Fetch the skills of a professional profile, with how many members endorsed each one

## Endpoint

```
GET /api/v1/profile/skills
```

## Authentication

Include your API key in the request header:

```bash theme={null}
X-API-Key: your-api-key-here
```

## How it works

[Get Profile](https://docs.fetchin.io/api-reference/endpoint/get-profile.md) already returns every skill with its name and `skillUrn`. This endpoint adds, for each skill, how many members endorsed it, so you know which skills have endorsers and how many pages [Get Profile Skill Endorsers](https://docs.fetchin.io/api-reference/endpoint/get-profile-skill-endorsers.md) will take for each.

A member lists 100 skills at most, so the default page of 100 holds them all.

## Parameters

**profileUrlOrUrn** `string` *required*

The professional profile whose skills to fetch. Accepts any of:

* **Profile URN**. Example: `urn:li:fsd_profile:ACoAAAAteRwB-gns09UDa-RYj02ngoedsj7C9QM`
* **Public identifier (slug)**. Example: `billcorbettauthor`
* **Profile URL**. Example: `https://www.linkedin.com/in/billcorbettauthor` (a trailing slash makes no difference)

Member profiles only: a company page URL or URN is rejected with a `400` error, free of charge.

**count** `integer` (default: 100)

Number of skills per page.

* Minimum: 1 — `0` or a negative value is rejected with `400`
* Maximum: 100 — a higher value is clamped, not rejected
* Default: 100. A call costs the same whatever the `count`.

**start** `integer` (default: 0)

Offset of the first skill to return. Pass `0` (or omit it) for the first page, then the `nextStart` the previous response gave you. See [Pagination](#pagination).

* Minimum: 0 — a negative value is rejected with `400`
* Maximum: 100,000 — a higher value is capped, not rejected

## Response

**skills** `array`

The member's skills, in the order the profile shows them.

**Skill object**

**name** `string`

Skill name, as the profile shows it. The same as `skills[].name` on [Get Profile](https://docs.fetchin.io/api-reference/endpoint/get-profile.md).

**skillUrn** `string`

The skill's id, the same as on [Get Profile](https://docs.fetchin.io/api-reference/endpoint/get-profile.md). Pass it to [Get Profile Skill Endorsers](https://docs.fetchin.io/api-reference/endpoint/get-profile-skill-endorsers.md) to list every member who endorsed the skill.

**endorsements** `integer`

How many members endorsed the skill, as the source counts them. The source stops counting at 99: a skill it shows as "99+" reads `99` and carries `endorsementsCapped`. `0` when nobody endorsed the skill. Absent when the count could not be read on this call.

**endorsementsCapped** `boolean`

Present and `true` only when the source shows "99+": the skill has more endorsements than `endorsements` says. Absent otherwise — never `false`. The full list is at [Get Profile Skill Endorsers](https://docs.fetchin.io/api-reference/endpoint/get-profile-skill-endorsers.md).

**hasMore** `boolean`

`true` when the profile has more skills after this page. `false` on the last page.

**nextStart** `integer`

The `start` value to request next. Present **only** when `hasMore` is `true`.

## Example Request

```bash cURL theme={null}
curl -X GET "https://api.fetchin.io/api/v1/profile/skills?profileUrlOrUrn=billcorbettauthor" \
  -H "X-API-Key: your-api-key-here"
```

```javascript JavaScript theme={null}
const response = await fetch(
  'https://api.fetchin.io/api/v1/profile/skills?profileUrlOrUrn=billcorbettauthor',
  { headers: { 'X-API-Key': 'your-api-key-here' } }
);

const data = await response.json();
console.log(data.skills);
```

```python Python theme={null}
import requests

response = requests.get(
    'https://api.fetchin.io/api/v1/profile/skills',
    params={'profileUrlOrUrn': 'billcorbettauthor'},
    headers={'X-API-Key': 'your-api-key-here'},
)

data = response.json()
print(data['skills'])
```

## Example Response

```json theme={null}
{
  "skills": [
    {
      "name": "Illustration",
      "skillUrn": "urn:li:fsd_skill:(ACoAAAAteRwB-gns09UDa-RYj02ngoedsj7C9QM,9)",
      "endorsements": 0
    },
    {
      "name": "Tweens",
      "skillUrn": "urn:li:fsd_skill:(ACoAAAAteRwB-gns09UDa-RYj02ngoedsj7C9QM,24)",
      "endorsements": 2
    },
    {
      "name": "Public Speaking",
      "skillUrn": "urn:li:fsd_skill:(ACoAAAAteRwB-gns09UDa-RYj02ngoedsj7C9QM,25)",
      "endorsements": 99,
      "endorsementsCapped": true
    }
  ],
  "hasMore": false
}
```

## Errors

See [Error Handling](https://docs.fetchin.io/concepts/error-handling.md) for the full list of error codes and recommended handling.

* `400` — `MISSING_PARAMETER` / `INVALID_PARAMETER` / `INVALID_URN`
* `401` — `INVALID_API_KEY`
* `402` — `QUOTA_EXHAUSTED`
* `404` — `PROFILE_NOT_FOUND`
* `429` — `RATE_LIMITED`
* `500` — `INTERNAL_ERROR`
* `503` — `SERVICE_UNAVAILABLE`. The page could not be fetched on this call: retry. A failed page is never returned as an empty list.

## Pagination

The list is paged by **offset**, up to 100 skills per page.

1. Request the first page with `start=0` (or omit `start`).
2. If `hasMore` is `true`, request the next page with the `nextStart` the response gave you.
3. Continue until `hasMore` is `false`.

With the default `count` of 100, one page holds every skill of a profile.

## Notes

> [!NOTE]
>
> This endpoint consumes 1 credit per call, whatever the `count`. Errors are free — with one exception: a `404` not found costs 1 credit. See [Quotas](https://docs.fetchin.io/concepts/quotas.md).

> [!TIP]
>
> To get every endorser of a profile: call this endpoint once, skip the skills with `endorsements: 0`, then page through [Get Profile Skill Endorsers](https://docs.fetchin.io/api-reference/endpoint/get-profile-skill-endorsers.md) for the others. A skill with `endorsements` under 100 fits in one page; one with `endorsementsCapped` takes two or more.


## OpenAPI

````yaml GET /api/v1/profile/skills
openapi: 3.1.0
info:
  title: Fetchin API
  version: 1.0.0
  description: Public B2B data API for fetching posts and profile information
servers:
  - url: https://api.fetchin.io
    description: Production server
security:
  - ApiKeyAuth: []
paths:
  /api/v1/profile/skills:
    get:
      summary: Get a profile's skills with their endorsement counts
      description: >-
        Fetch the skills of a profile, each with its `skillUrn` and how many
        members endorsed it, up to 100 per page (a member lists 100 skills at
        most, so the default page holds them all). Paged by offset: keep paging
        with `nextStart` while `hasMore` is true. One credit per call. To list
        the members behind a count, pass the `skillUrn` to GET
        /api/v1/profile/skills/endorsers.
      parameters:
        - name: profileUrlOrUrn
          in: query
          required: true
          description: >-
            The professional profile. Accepts a profile URN (example
            `urn:li:fsd_profile:ACoAAAAteRwB-gns09UDa-RYj02ngoedsj7C9QM`), a
            public identifier/slug (example `billcorbettauthor`), or a full
            profile URL (example
            `https://www.linkedin.com/in/billcorbettauthor`; a trailing slash is
            ignored). Member profiles only: a company page URL or URN is
            rejected with a `400` error, free of charge.
          schema:
            type: string
            example: billcorbettauthor
        - name: count
          in: query
          required: false
          description: >-
            Number of entries per page (default: 100, the most a page holds). A
            value above 100 is clamped to 100, not rejected. A call costs the
            same whatever the count.
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 100
        - name: start
          in: query
          required: false
          description: >-
            Offset of the first entry to return. Pass 0 (or omit) for the first
            page, then the `nextStart` of the previous response. Keep paging
            while `hasMore` is true.
          schema:
            type: integer
            minimum: 0
            default: 0
      responses:
        '200':
          description: Successfully fetched the skills
          content:
            application/json:
              schema:
                type: object
                properties:
                  skills:
                    type: array
                    description: The member's skills, in the order the profile shows them.
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                          example: Public Speaking
                          description: >-
                            Skill name, as the profile shows it (the same as
                            `skills[].name` on GET /api/v1/profile).
                        skillUrn:
                          type: string
                          example: >-
                            urn:li:fsd_skill:(ACoAAAAteRwB-gns09UDa-RYj02ngoedsj7C9QM,25)
                          description: >-
                            The skill's id, the same as on GET /api/v1/profile:
                            pass it to GET /api/v1/profile/skills/endorsers.
                        endorsements:
                          type: integer
                          minimum: 0
                          maximum: 99
                          example: 99
                          description: >-
                            How many members endorsed the skill, as the source
                            counts them: it stops at 99, so a skill shown as 99+
                            reads 99 and carries `endorsementsCapped`. 0 when
                            nobody endorsed it. Absent when the count could not
                            be read on this call.
                        endorsementsCapped:
                          type: boolean
                          enum:
                            - true
                          description: >-
                            Present and true only when the source shows 99+: the
                            skill has more endorsements than `endorsements`
                            says. Absent otherwise, never false.
                  hasMore:
                    type: boolean
                    description: >-
                      True when the source has more entries after this page,
                      false on the last page.
                  nextStart:
                    type: integer
                    example: 100
                    description: >-
                      The `start` value to request next. Present only when
                      `hasMore` is true.
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
components:
  responses:
    BadRequest:
      description: Bad request - a required parameter is missing or a value is invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missingParameter:
              value:
                error: Missing profileUrlOrUrn parameter
                code: MISSING_PARAMETER
            invalidParameter:
              value:
                error: limit must be a number between 1 and 100
                code: INVALID_PARAMETER
            invalidUrn:
              value:
                error: 'Invalid URN format: urn:li:member:123456789'
                code: INVALID_URN
    Unauthorized:
      description: Unauthorized - the X-API-Key header is missing or invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missingApiKey:
              value:
                error: Missing API key. Include X-API-Key header.
                code: UNAUTHENTICATED
            invalidApiKey:
              value:
                error: Invalid API key
                code: INVALID_API_KEY
    PaymentRequired:
      description: >-
        Payment required - you are out of credits (QUOTA_EXHAUSTED): the plan
        allowance for the current period is spent AND no pay-as-you-go credits
        are left. Buy pay-as-you-go credits (self-serve,
        https://fetchin.io/credits), upgrade your plan, or wait for renewal;
        retrying will not help and no Retry-After is sent. Distinct from the
        per-second rate limit, which is a 429 (RATE_LIMITED).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            quotaExhausted:
              value:
                error: Quota exceeded. Upgrade your plan or wait for renewal.
                code: QUOTA_EXHAUSTED
                details:
                  creditsRemaining: 0
                  subscriptionCreditsRemaining: 0
                  paygCreditsRemaining: 0
                  buyCreditsUrl: https://fetchin.io/credits
    NotFound:
      description: >-
        Not found - the request was served successfully but the requested
        professional entity does not exist.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            profileNotFound:
              value:
                error: LinkedIn profile not found
                code: PROFILE_NOT_FOUND
            postNotFound:
              value:
                error: Unable to find post from url
                code: POST_NOT_FOUND
            companyNotFound:
              value:
                error: Unable to find company slug from urn
                code: COMPANY_NOT_FOUND
    TooManyRequests:
      description: >-
        Too many requests - your per-second rate limit (RATE_LIMITED) was
        exceeded. Back off and retry, honoring the Retry-After header. (Running
        out of credits is a separate 402 PaymentRequired / QUOTA_EXHAUSTED, and
        buying credits never raises this per-second limit.)
      headers:
        Retry-After:
          description: Number of seconds to wait before retrying the request.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            rateLimited:
              value:
                error: Rate limit exceeded. Try again later.
                code: RATE_LIMITED
    InternalError:
      description: >-
        Internal server error - an unexpected error occurred in our API. Safe to
        retry; contact support if it persists.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            internalError:
              value:
                error: Internal server error
                code: INTERNAL_ERROR
    ServiceUnavailable:
      description: >-
        Service unavailable - our API is temporarily unable to serve this
        request (capacity exhausted or overloaded). This is on our side, not
        yours and not an upstream service outage. Retry with backoff, honoring
        the Retry-After header.
      headers:
        Retry-After:
          description: Number of seconds to wait before retrying the request.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            serviceUnavailable:
              value:
                error: >-
                  Service temporarily unable to serve this request. Please
                  retry.
                code: SERVICE_UNAVAILABLE
  schemas:
    Error:
      type: object
      required:
        - error
        - code
      properties:
        error:
          type: string
          description: >-
            Human-readable description of what went wrong. For display/logging;
            do not branch on this string.
        code:
          type: string
          enum:
            - MISSING_PARAMETER
            - INVALID_PARAMETER
            - INVALID_URN
            - UNAUTHENTICATED
            - INVALID_API_KEY
            - FORBIDDEN
            - NOT_FOUND
            - PROFILE_NOT_FOUND
            - POST_NOT_FOUND
            - COMPANY_NOT_FOUND
            - ENDPOINT_REMOVED
            - RATE_LIMITED
            - QUOTA_EXHAUSTED
            - INTERNAL_ERROR
            - UPSTREAM_ERROR
            - SERVICE_UNAVAILABLE
            - UPSTREAM_TIMEOUT
          description: >-
            Stable, machine-readable error code. Branch your integration on this
            value, not on the HTTP status alone or the message text.
        details:
          type: object
          additionalProperties: true
          description: >-
            Optional structured context. On QUOTA_EXHAUSTED it carries the
            balance breakdown at the moment of the rejection - creditsRemaining,
            subscriptionCreditsRemaining, paygCreditsRemaining - plus
            buyCreditsUrl, the self-serve page for buying more credits. Always
            additive and never guaranteed: read it for diagnostics and logs, but
            branch on `code` and do not require any field of it.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.