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

# Stake a token

> Discover a staking yield, decide validator behavior, enter, sign, track, and exit through the Yield API.

Stake a token through the same Stake It model used across Earn: discover the yield, read its mechanics, resolve validator selection, enter, sign and submit, then track the position through exit. This guide walks the full loop with a validator-less liquid-staking example (Lido ETH) and a validator-selection example (Solana native staking) side by side.

## Prerequisites

* A yield.xyz API key, sent as `x-api-key` on every request.
* A signer for the target chain. See [Transaction signing](/guides/transaction-signing/index) for EVM and Solana-specific signing code.
* Familiarity with the generic loop in [Enter and exit flows](/guides/earn/enter-exit-flows): this guide covers what's specific to staking and links back to that guide for branching on `executionPattern`, choosing a submit path, and polling.

<Note>
  We construct. You sign. Every action returns unsigned transactions; yield.xyz never holds keys and never signs on your behalf. Inspect `unsignedTransaction` before signing it.
</Note>

## What's specific to staking

The flow is the [Stake It flow](/documentation/earn/staking/index#the-stake-it-flow): discover, inspect, resolve the validator, enter, sign, track, read balances. What changes for staking yields:

* **Discovery.** Filter with `types=staking` and add `liquid_staking` (`types=staking,liquid_staking`) to include liquid staking yields. Add `providers` when you know the protocol: the filter matches each yield's `mechanics.type`, so don't assume which type a given protocol reports.
* **Validator behavior.** Read `mechanics.requiresValidatorSelection` and look for a `validatorAddress` or `validatorAddresses` field in `mechanics.arguments.enter.fields`. When one is present, fetch candidates from `GET /v1/yields/{yieldId}/validators` and pass the chosen address back. [Validator selection](/guides/earn/staking/validator-selection) covers the full decision, including provider-assigned validators.
* **Limits and timing.** Read `mechanics.entryLimits` (`minimum`, `maximum`, `subsequentMinimum`) before building the amount, and `mechanics.cooldownPeriod` for the unbonding window. Read both per yield rather than assuming fixed numbers; they vary by integration and can change.
* **Position lifecycle.** A staking position moves through `entering`, `active`, `exiting` (during the unbonding cooldown after exit) and `withdrawable` (cooldown complete, with a `WITHDRAW`- or `CLAIM`-family pending action). You run that pending action to receive the funds.

For the generic branch, sign, submit and poll loop, see [Enter and exit flows](/guides/earn/enter-exit-flows#enter-a-position).

## Examples

<Tabs>
  <Tab title="EVM: Lido ETH">
    `ethereum-eth-lido-staking` is liquid staking: it takes no validator argument, and mints stETH for the ETH you deposit.

    ```bash cURL theme={null}
    curl --request GET \
      --url https://api.yield.xyz/v1/yields/ethereum-eth-lido-staking \
      --header "x-api-key: $YIELD_API_KEY"
    ```

    ```json Response (excerpt) theme={null}
    {
      "id": "ethereum-eth-lido-staking",
      "status": { "enter": true, "exit": true },
      "outputToken": { "symbol": "stETH" },
      "mechanics": {
        "requiresValidatorSelection": false,
        "arguments": {
          "enter": { "fields": [{ "name": "amount", "required": true }] },
          "exit": { "fields": [{ "name": "amount", "required": true }] }
        }
      }
    }
    ```

    `requiresValidatorSelection` is `false` and neither schema names a validator field, so skip validator selection entirely. Check `mechanics.cooldownPeriod` on this response for the exit unbonding window currently in effect; it is not a fixed constant.

    <Steps>
      <Step title="Enter">
        ```bash cURL theme={null}
        curl --request POST \
          --url https://api.yield.xyz/v1/actions/enter \
          --header "x-api-key: $YIELD_API_KEY" \
          --header 'content-type: application/json' \
          --data '{
            "yieldId": "ethereum-eth-lido-staking",
            "address": "0xYOUR_WALLET_ADDRESS",
            "arguments": { "amount": "1" }
          }'
        ```

        This returns a single `STAKE` transaction (ETH is the native gas token, so there's no approval step). Sign and submit it, then poll to a terminal status as in [Enter and exit flows](/guides/earn/enter-exit-flows#enter-a-position).
      </Step>

      <Step title="Exit">
        ```bash cURL theme={null}
        curl --request POST \
          --url https://api.yield.xyz/v1/actions/exit \
          --header "x-api-key: $YIELD_API_KEY" \
          --header 'content-type: application/json' \
          --data '{
            "yieldId": "ethereum-eth-lido-staking",
            "address": "0xYOUR_WALLET_ADDRESS",
            "arguments": { "amount": "1" }
          }'
        ```

        Exiting returns two transactions: an `APPROVAL` (`SKIPPED` if your stETH allowance to Lido's withdrawal queue already covers the amount) at `stepIndex` 0, then an `UNSTAKE` at `stepIndex` 1 that places a withdrawal request in Lido's own queue. This does not release ETH yet; it starts the cooldown you read from `mechanics.cooldownPeriod`.
      </Step>

      <Step title="Claim after cooldown">
        Once the cooldown ends, `POST /v1/yields/ethereum-eth-lido-staking/balances` reports the position as `withdrawable` with a `CLAIM_UNSTAKED` pending action. Execute it through `POST /v1/actions/manage`, passing `action: "CLAIM_UNSTAKED"` and the `passthrough` copied unchanged from the balance response, the same pattern as any other pending action. See [Enter and exit flows](/guides/earn/enter-exit-flows#complete-a-pending-action-manage).
      </Step>
    </Steps>

    <Warning>
      `ethereum-eth-lido-staking` supports both enter and exit. Two related Lido yields, `ethereum-matic-liquid-staking` (stMATIC) and `solana-sol-lido-staking` (stSOL), are exit only: `status.enter` is always `false` on both, so they only unwind existing positions. Don't route new deposits to them. See [Lido](/documentation/earn/defi/liquid-staking/lido).
    </Warning>
  </Tab>

  <Tab title="Solana: native staking">
    `solana-sol-native-multivalidator-staking` is native delegation: it requires `validatorAddress` on both enter and exit.

    ```bash cURL theme={null}
    curl --request GET \
      --url https://api.yield.xyz/v1/yields/solana-sol-native-multivalidator-staking \
      --header "x-api-key: $YIELD_API_KEY"
    ```

    ```json Response (excerpt) theme={null}
    {
      "id": "solana-sol-native-multivalidator-staking",
      "status": { "enter": true, "exit": true },
      "mechanics": {
        "requiresValidatorSelection": true,
        "arguments": {
          "enter": {
            "fields": [
              { "name": "amount", "required": true },
              { "name": "validatorAddress", "required": true }
            ]
          },
          "exit": {
            "fields": [
              { "name": "amount", "required": true },
              { "name": "validatorAddress", "required": true }
            ]
          }
        }
      }
    }
    ```

    `requiresValidatorSelection: true` and the schema names the singular `validatorAddress` on both actions, not `validatorAddresses`: this yield's own multi-validator support comes from making one enter call per validator, not from an array argument. Resolve the address first; see [Validator selection](/guides/earn/staking/validator-selection).

    <Steps>
      <Step title="Fetch a validator">
        ```bash cURL theme={null}
        curl --request GET \
          --url 'https://api.yield.xyz/v1/yields/solana-sol-native-multivalidator-staking/validators?preferred=true&status=active' \
          --header "x-api-key: $YIELD_API_KEY"
        ```

        Take one candidate's `address` from the response.
      </Step>

      <Step title="Enter">
        ```bash cURL theme={null}
        curl --request POST \
          --url https://api.yield.xyz/v1/actions/enter \
          --header "x-api-key: $YIELD_API_KEY" \
          --header 'content-type: application/json' \
          --data '{
            "yieldId": "solana-sol-native-multivalidator-staking",
            "address": "YOUR_SOLANA_ADDRESS",
            "arguments": { "amount": "1", "validatorAddress": "VALIDATOR_VOTE_ACCOUNT" }
          }'
        ```

        This creates a new on-chain stake account delegated to `validatorAddress`. Sign and submit it, then poll as in [Enter and exit flows](/guides/earn/enter-exit-flows#enter-a-position); see [Transaction signing › Solana](/guides/transaction-signing/solana) for signing.
      </Step>

      <Step title="Exit">
        ```bash cURL theme={null}
        curl --request POST \
          --url https://api.yield.xyz/v1/actions/exit \
          --header "x-api-key: $YIELD_API_KEY" \
          --header 'content-type: application/json' \
          --data '{
            "yieldId": "solana-sol-native-multivalidator-staking",
            "address": "YOUR_SOLANA_ADDRESS",
            "arguments": { "amount": "1", "validatorAddress": "VALIDATOR_VOTE_ACCOUNT" }
          }'
        ```

        Exit requires the same `validatorAddress` used on enter: it deactivates the matching stake account (splitting it first if only part of the delegation exits). The position moves to `exiting`, then `withdrawable` once the network's unbonding period ends.
      </Step>

      <Step title="Withdraw after unbonding">
        Once `withdrawable`, run the `WITHDRAW` (one stake account) or `WITHDRAW_ALL` (every unstaked stake account) pending action through `POST /v1/actions/manage`. Unstaked SOL isn't spendable until you do: see [Solana staking](/documentation/earn/staking/non-evm/solana#managing-the-position) for the full pending-action table.
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Verify

After the last transaction of any step confirms, re-read the balance and check both `type` and `pendingActions` rather than assuming the position is fully settled:

```bash cURL theme={null}
curl --request POST \
  --url https://api.yield.xyz/v1/yields/{yieldId}/balances \
  --header "x-api-key: $YIELD_API_KEY" \
  --header 'content-type: application/json' \
  --data '{ "address": "YOUR_ADDRESS" }'
```

An `active` balance with an empty `pendingActions` array means the stake is fully entered and earning. An `exiting` or `withdrawable` balance means the position is mid-unbond or ready for its claim or withdraw pending action; don't tell a user their funds have arrived until the balance itself says `withdrawable` and the pending action has been run.

## Next steps

<Columns cols={2}>
  <Card title="Validator selection" icon="sitemap" href="/guides/earn/staking/validator-selection">
    The full decision flow, the validators endpoint, and Preferred vs BYON vs Whitelabel.
  </Card>

  <Card title="Enter and exit flows" icon="arrow-right-arrow-left" href="/guides/earn/enter-exit-flows">
    The generic branch, sign, submit, and poll loop this guide builds on.
  </Card>

  <Card title="Tracking balances" icon="wallet" href="/guides/earn/tracking-balances">
    Build portfolio views across staking, liquid-staking, and other Earn positions.
  </Card>

  <Card title="Transaction signing" icon="pen-nib" href="/guides/transaction-signing/index">
    Chain-specific signing code, including EVM and Solana.
  </Card>
</Columns>


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