Startnext API for Developers

The Startnext API allows you to integrate project data into your website, app, or other applications. This documentation shows you how to use the API.

A note on wording: what this guide calls a reward is named incentive throughout the API — in paths (/incentives), scopes (incentives:read) and response schemas. Both refer to the same thing: what a project offers its supporters in return for their support.

Quick Start

The Startnext API uses two types of API keys for external access:

Key type Prefix Use case
Project API Key snx_proj_* Access data of a specific project you manage
User API Key snx_user_* Access your own projects programmatically

1a. Create a Project API Key

To access data for a specific project, you need a Project API Key:

  1. Open your project on Startnext
  2. Go to Project InterfaceAdministrationAPI Keys
  3. Click Create New API Key
  4. Select the required permissions (scopes)
  5. Copy the displayed API key and store it securely

Important: The full API key is only shown once at creation. It starts with snx_proj_ followed by a random string.

1b. Create a User API Key

To access your own projects programmatically, you need a User API Key:

  1. Log in to Startnext
  2. Go to SettingsAPI & Integrations
  3. Click Create New API Key
  4. Enter a name — the user:read scope is always included automatically
  5. Copy the displayed API key — it starts with snx_user_

Important: The full API key is only shown once at creation.

2. First API Request

# Project-specific data (Project API Key)
curl -H "Authorization: Bearer snx_proj_YOUR_API_KEY" \
     "https://www.startnext.com/myty/api/crowdfunding/project/your-project"

# Your own and team projects (User API Key)
curl -H "Authorization: Bearer snx_user_YOUR_API_KEY" \
     "https://www.startnext.com/myty/api/crowdfunding/user/me/projects"

Terms of Use

By using the Startnext API, you agree to the Startnext API Terms of Use.

Base URL

All API endpoints start with:

https://www.startnext.com/myty/api/crowdfunding

Authentication

Add your API key as a Bearer Token in the Authorization header:

Authorization: Bearer snx_proj_xxxxxxxxxxxxxxxx

Authorization: Bearer is the only accepted channel.

Available Permissions (Scopes)

Project API Key Scopes

When creating a Project API Key, select which project data may be accessed:

Scope Description
blog:read Blog entries/updates of the project
fundings:read Public supports for streams (timestamp and name)
incentives:read Project rewards
orders:read Orders (supports) with full details
project:read Basic project data (title, description, funding status)
subscribers:read Subscribers of the project
wall:read Wall posts of the project

Note: Project API Keys require your project to be on the Pro scope or higher. Basic/Legacy projects cannot create API keys.

User API Key Scopes

A User API Key is an end-user credential equivalent to an OAuth 2.0 access token: it draws from the same scope catalog and works on the same endpoints as OAuth. When creating a User API Key, select which data may be accessed (user:read is always included and cannot be deselected):

Scope Always enabled Description
user:read Read your own profile/identity and team invitations (/user/me/identity) — always included
user:write Update your profile data, e.g. profile image (/user/me/image)
project:read Read your own and team projects incl. team, blog, timeline, incentives and funding (/user/me/projects, /user/me/incentives)
project:write Create and manage your own and team projects incl. team, blog, timeline, incentives and images (POST /project)
contacts:read Read personal contact data of supporters and subscribers (orders, subscriber emails)

API Endpoints

Retrieve Project Data

GET /project/{link_caption}

Scope: project:read

Returns the basic project information.

Example:

curl -H "Authorization: Bearer snx_proj_xxx" \
     "https://www.startnext.com/myty/api/crowdfunding/project/my-awesome-project"

Response:

{
    "project": {
        "id": 12345,
        "title": "My Awesome Project",
        "subtitle": "A project for everyone",
        "link_caption": "my-awesome-project",
        "status": "active",
        "currency": "EUR",
        "current_funding": 7550.00,
        "total_funding": 7550.00,
        "funding_threshold": 10000.00,
        "project_type_all_or_nothing": true,
        "supporter_count": 150,
        "like_count": 89,
        "teaser_text": "Short description...",
        "url": {
            "main": "https://www.startnext.com/my-awesome-project",
            "main_relative": "/my-awesome-project",
            "comments": "https://www.startnext.com/my-awesome-project/comments",
            "faq": "https://www.startnext.com/my-awesome-project/faq",
            "widget": "https://www.startnext.com/my-awesome-project/widget",
            "localized_urls": {
                "de": "/my-awesome-project",
                "en": "/en/my-awesome-project"
            }
        }
    },
    "status": 0
}

There is no url.support — build the support link from url.main. url.widget points at the ready-made embed widget, which is often simpler than building your own funding display.

The top-level status field is legacy. It is a hard-coded 0 on every successful response and carries no information — do not branch on it. Use the HTTP status code instead. Note that it is unrelated to project.status, which holds the project's actual phase (new, started, active, successful, …).


List Rewards

GET /project/{link_caption}/incentives

Scope: incentives:read

Returns all rewards of the project, including hidden ones (manually hidden via status=hidden or hidden due to an unmet funding goal). Use hidden_until_goal_reached and is_unlocked on each reward to determine its visibility state.

Parameters:

Parameter Type Description
limit Integer Maximum number of results (default: 20, max: 100)
offset Integer Starting position for pagination (default: 0)

Example:

curl -H "Authorization: Bearer snx_proj_xxx" \
     "https://www.startnext.com/myty/api/crowdfunding/project/my-project/incentives?limit=10"

Response:

{
    "data": [
        {
            "id": 12345,
            "title": "Limited T-Shirt",
            "description": "An exclusive T-shirt for supporters",
            "price": 25.00,
            "quantity_total": 100,
            "quantity_remaining": 42,
            "quantity_sold": 58,
            "status": "enabled",
            "address_required": true,
            "is_available": true,
            "is_unlocked": true,
            "hidden_until_goal_reached": false
        }
    ],
    "meta": {
        "total": 15,
        "limit": 10,
        "offset": 0
    }
}

quantity_total: 0 means unlimited. Combine hidden_until_goal_reached with is_unlocked to determine whether a reward is currently purchasable.


List Blog Entries

GET /project/{link_caption}/blog

Scope: blog:read

Returns all blog entries/updates of the project.

Query parameters: blog_type (text|story, default text), offset (default 0), limit (default 20, max 100), include_supporter_only (0|1, default 0).

Supporter-only entries: Blog entries flagged is_supporter_only are excluded by default. They are only returned when the request sets include_supporter_only=1 and the caller is verified server-side as allowed to see them (an OAuth Starter/Manager token, or a logged-in starter, team member, platform manager or supporter of the project). A public project API key alone never unlocks them. Requests with include_supporter_only=1 bypass the CDN cache.

Example:

curl -H "Authorization: Bearer snx_proj_xxx" \
     "https://www.startnext.com/myty/api/crowdfunding/project/my-project/blog"

Response:

{
    "data": [
        {
            "id": 5678,
            "title": "We made it!",
            "text": "Thanks to all supporters...",
            "type": "text",
            "status": "enabled",
            "release_timestamp": 1705315800
        }
    ],
    "meta": {
        "total": 5,
        "limit": 20,
        "offset": 0
    }
}

List Orders

GET /project/{link_caption}/orders

Scope: orders:read

Note: This endpoint is only available once the project has been successfully funded (≥100%).

Returns all successful supports with full details.

When the data is final. Only supports with status success are returned. Until the project is paid out, a support can still leave that status (e.g. a failed collection) and then drops out of this list. After payout — process_status is payoff on the project — supports are no longer modified: a chargeback or a supporter withdrawing at that point is handled outside Startnext and does not change the support here. Use transaction_number as the key for matching, and updated_timestamp to detect changes while the project is still in collection.

Example:

curl -H "Authorization: Bearer snx_proj_xxx" \
     "https://www.startnext.com/myty/api/crowdfunding/project/my-project/orders"

Response:

{
    "data": [
        {
            "support_timestamp": 1705315800,
            "updated_timestamp": 1705402200,
            "transaction_number": "TR1234567890ST",
            "total_amount": 50.00,
            "status_code": "collected",
            "bill_address": {
                "firstname": "Jane",
                "lastname": "Doe",
                "city": "Berlin",
                "country": "Germany"
            },
            "ordered_incentives": [
                {
                    "id": 12345,
                    "count": 2,
                    "price_single": 25.00,
                    "price_total": 50.00
                }
            ]
        }
    ],
    "meta": {
        "total": 150,
        "limit": 20,
        "offset": 0
    }
}

Field naming: the address objects use firstname / lastname, whereas user objects on other endpoints use first_name / last_name. This inconsistency exists in the API itself.


List Subscribers

GET /project/{link_caption}/subscribers

Scope: subscribers:read

Returns all subscribers of the project.

Example:

curl -H "Authorization: Bearer snx_proj_xxx" \
     "https://www.startnext.com/myty/api/crowdfunding/project/my-project/subscribers"

Response:

{
    "data": [
        {
            "subscribed_timestamp": 1705315800,
            "is_news_subscribed": true,
            "user": {
                "id": 789,
                "first_name": "Jane",
                "last_name": "Doe",
                "is_public": true
            }
        }
    ],
    "meta": {
        "total": 89,
        "limit": 20,
        "offset": 0
    }
}

user may be null for subscribers without a Startnext account. Note that this endpoint returns first_name / last_name and has no display_name field.


List Wall Posts

GET /project/{link_caption}/wall

Scope: wall:read

Returns all wall posts of the project.

Example:

curl -H "Authorization: Bearer snx_proj_xxx" \
     "https://www.startnext.com/myty/api/crowdfunding/project/my-project/wall"

Response:

{
    "data": [
        {
            "id": 12345,
            "message": "Great project! Good luck!",
            "status": "enabled",
            "created_timestamp": 1705315800,
            "user": {
                "id": 789,
                "first_name": "Jane",
                "last_name": "Doe",
                "display_name": "Jane Doe"
            }
        }
    ],
    "meta": {
        "total": 25,
        "limit": 20,
        "offset": 0
    }
}

Public Support Stream

GET /project/{link_caption}/fundings

Scope: fundings:read

Returns publicly visible supports with minimal data. Intended for live streams or feeds of recent supporters.

Note: For detailed order data (addresses, rewards, etc.), use the /orders endpoint with the orders:read scope instead.

Example:

curl -H "Authorization: Bearer snx_proj_xxx" \
     "https://www.startnext.com/myty/api/crowdfunding/project/my-project/fundings"

Response:

{
    "data": [
        {
            "support_timestamp": 1705315800,
            "user": {
                "id": 789,
                "display_name": "Jane Doe"
            }
        }
    ],
    "meta": {
        "total": 150,
        "limit": 20,
        "offset": 0
    }
}

This endpoint returns only support_timestamp and user per entry — nothing else.


OpenAPI Specification

GET /crowdfunding/openapi.json

Auth: None (public endpoint)

Returns the complete OpenAPI specification for all public API endpoints (Project API Key and User API Key endpoints) as JSON. Useful for MCP clients, API explorers, and code generation.

Example:

curl "https://www.startnext.com/myty/api/crowdfunding/openapi.json"

Get API Key Identity

GET /user/me/identity

Credential: User API Key or OAuth Bearer token Scope: user:read (always included in every credential)

Returns minimal identity data for the authenticated credential: the user ID, URL slug, and the list of active scopes. Contains no PII. Intended for verifying an end-user credential — e.g. by MCP clients — and for discovering which permissions it has.

Example:

curl -H "Authorization: Bearer snx_user_xxx" \
     "https://www.startnext.com/myty/api/crowdfunding/user/me/identity"

Response:

{
    "data": {
        "id": 42,
        "link_caption": "jane-doe",
        "scopes": [
            {
                "scope": "user:read",
                "endpoints": [
                    "GET /myty/api/crowdfunding/user/me/identity"
                ]
            },
            {
                "scope": "project:read",
                "endpoints": [
                    "GET /myty/api/crowdfunding/user/me/projects"
                ]
            }
        ]
    }
}

Get Incentives from Own and Team Projects

GET /user/me/incentives

Credential: User API Key or OAuth Bearer token Scope: project:read

Returns a paginated list of all active incentives (rewards) from projects where the authenticated user is the initiator or a team member.

Parameters:

Parameter Type Description
sort String Sort order: sold (default), new, price-a, price-d, name-a, name-d, rand
fundable Boolean Filter by fundable projects only
is_favorite Boolean Filter favorites only
limit Integer Maximum number of results (default: 20, max: 100)
offset Integer Starting position for pagination (default: 0)

Example:

curl -H "Authorization: Bearer snx_user_xxx" \
     "https://www.startnext.com/myty/api/crowdfunding/user/me/incentives"

Response:

{
    "data": [
        {
            "id": 12345,
            "title": "Limited T-Shirt",
            "price": 25.00,
            "is_available": true,
            "quantity_total": 100,
            "quantity_remaining": 42,
            "project": {
                "id": 678,
                "title": "My Awesome Project",
                "link_caption": "my-awesome-project"
            }
        }
    ],
    "meta": {
        "total": 5,
        "limit": 20,
        "offset": 0
    }
}

List Own and Team Projects

GET /user/me/projects

Credential: User API Key or OAuth Bearer token Scope: project:read

Returns a paginated list of all projects where the authenticated user is the initiator or a team member — including drafts, regardless of the authentication path. Accepts a User API Key or OAuth Bearer token with the project:read scope, or a session cookie.

Parameters:

Parameter Type Description
sort String Sort order: project-end-date-d (default), project-end-date-a, project-activation-date-d, project-activation-date-a, project-title-d, project-title-a, project-funding-sum-d, project-funding-sum-a, project-support-count-d, project-support-count-a, project-fan-count-d, project-fan-count-a, rand
limit Integer Maximum number of results (default: 20, max: 100)
offset Integer Starting position for pagination (default: 0)

Example:

curl -H "Authorization: Bearer snx_user_xxx" \
     "https://www.startnext.com/myty/api/crowdfunding/user/me/projects?sort=project-activation-date-d&limit=20"

Response:

{
    "data": [
        {
            "id": 12345,
            "title": "My Awesome Project",
            "link_caption": "my-awesome-project",
            "status": "active",
            "currency": "EUR",
            "current_funding": 7550.00,
            "funding_threshold": 10000.00,
            "supporter_count": 150
        }
    ],
    "meta": {
        "total": 3,
        "limit": 20,
        "offset": 0
    }
}

Data Formats

Dates

All dates are returned as UNIX timestamps (seconds since 1970-01-01):

{
    "created_timestamp": 1705315800,
    "support_timestamp": 1705402200
}

Convert in JavaScript:

const date = new Date(timestamp * 1000);

Convert in PHP:

$date = new DateTime('@' . $timestamp);

Pagination

Paginated endpoints support the limit and offset parameters:

curl -H "Authorization: Bearer snx_proj_xxx" \
     "https://www.startnext.com/myty/api/crowdfunding/project/my-project/incentives?limit=10&offset=20"

Edge cases — none of these return 400:

Input Behaviour
limit above 100 Silently clamped to 100. Always page until offset + len(data) >= meta.total; never assume your requested page size was honoured — check meta.limit.
limit=0 or limit= (empty) Treated as absent → falls back to the default of 20
offset=0 or offset= (empty) Falls back to 0 (same result)

The response contains a meta object with pagination information. meta.limit is the limit that was actually applied — compare it against what you sent:

{
    "data": [
        ...
    ],
    "meta": {
        "total": 150,
        "limit": 10,
        "offset": 20
    }
}

Rate Limiting

API requests are limited to ensure system stability. Which limit applies depends on the credential you use, not on the endpoint.

The values below are the platform defaults and may be adjusted per environment. Do not hard-code them — read the current values from the response headers instead (see below).

Project API Key

Limits depend on the plan of the project the key belongs to:

Project plan Per minute Per day
Pro 120 10,000
Premium 300 50,000
Enterprise 600 100,000

A key belonging to a project on any other plan (Basic, Legacy) falls back to the unauthenticated limits below.

User API Key

Credential Per minute Per day
User API Key 60 5,000

These limits are per key and independent of any project plan — a User API Key that reaches projects on the Premium plan is still limited to 60 requests per minute.

Unauthenticated requests

Endpoints that work without a credential are limited per IP address:

Per minute Per day
No credential 30 1,000

Rate Limit Headers

Responses authenticated with a Project or User API Key carry the current limit status:

X-RateLimit-Day-Limit: 10000
X-RateLimit-Day-Remaining: 9850
X-RateLimit-Day-Reset: 1705363200
X-RateLimit-Minute-Limit: 120
X-RateLimit-Minute-Remaining: 118
X-RateLimit-Minute-Reset: 1705320060

Unauthenticated responses use the same six fields with a X-RateLimit-Public- prefix (X-RateLimit-Public-Minute-Limit, X-RateLimit-Public-Day-Remaining, …). The *-Reset fields are UNIX timestamps.

When the Limit Is Exceeded

When the limit is reached, you receive an HTTP 429. The wait time is in the Retry-After header (seconds) — it is not part of the response body:

HTTP/1.1 429 Too Many Requests
Retry-After: 45
{
    "error": "rate_limit_exceeded",
    "error_description": "Rate limit exceeded (minute). Please wait 45 seconds before retrying."
}

Read your backoff from the Retry-After header. error_description names the limit that was exceeded (minute or day), but it is a human-readable message — do not parse it.

Error Handling

HTTP Status Codes

Code Meaning
200 Success
400 Bad request
401 Invalid or missing API key
403 Missing permission (scope)
404 Project not found
429 Rate limit exceeded
500 Server error

Error Response

All errors use the same envelope. The HTTP status code is only in the status line — there is no status field in the body.

{
    "error": "unauthorized",
    "error_description": null
}
{
    "error": "insufficient_scope",
    "error_description": "This endpoint requires the \"fundings:read\" scope"
}

Branch on error — it is a stable, machine-readable code. error_description is a human-readable message that may be null and may change without notice.

Versioning and Deprecation

Be aware of what the API does not currently guarantee, so you can plan accordingly:

Practical consequence: fetch the OpenAPI specification periodically and diff it — that is currently the only reliable way to notice that a field, an endpoint or a default has changed. Code defensively: treat unknown response fields as additive rather than failing on them, and do not depend on field order or on the exact wording of error_description.

Retries and Idempotency

There is no Idempotency-Key support. Whether a retry is safe depends on the endpoint:

Operation Safe to retry? What a retry does
All GET endpoints ✅ Yes Read-only, no side effects
POST /user/me/image (user:write) ✅ Yes Replaces the profile image — repeating it yields the same end state
Single-slot image uploads (project title, logo, story) ✅ Yes Replaces the image in that slot
Gallery image uploads (project, reward, blog) No Appends. A retry adds a duplicate image and counts against the limit of 5 per gallery (422 gallery_full)
POST /project (project:write) No Creates another project. There is no deduplication — a retried request after a timeout leaves you with two projects, and it counts against a daily creation limit (default 10, then 429 rate_limit_exceeded)

For the non-idempotent operations: on a timeout or a 5xx, do not blind-retry. Read back the current state first (GET /user/me/projects or the relevant gallery endpoint) and only retry if the object is genuinely missing.

429 is always safe to retry — wait for Retry-After first. 4xx other than 429 will not succeed on retry; fix the request instead.

Code Examples

JavaScript (fetch)

const API_KEY = 'snx_proj_xxxxxxxx';
const PROJECT = 'my-project';

async function getProjectData() {
    const response = await fetch(
        `https://www.startnext.com/myty/api/crowdfunding/project/${PROJECT}`,
        {
            headers: {
                'Authorization': `Bearer ${API_KEY}`,
                'Accept': 'application/json'
            }
        }
    );

    if (!response.ok) {
        const error = await response.json();
        throw new Error(error.message || `HTTP ${response.status}`);
    }

    return response.json();
}

// Usage
getProjectData()
    .then(data => {
        console.log(`Funding: ${data.project.current_funding} €`);
        console.log(`Supporters: ${data.project.supporter_count}`);
    })
    .catch(error => console.error('Error:', error));

PHP (cURL)

<?php
$apiKey = 'snx_proj_xxxxxxxx';
$project = 'my-project';

function getProjectData($project, $apiKey) {
    $ch = curl_init();

    curl_setopt_array($ch, [
        CURLOPT_URL => "https://www.startnext.com/myty/api/crowdfunding/project/{$project}",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            "Authorization: Bearer {$apiKey}",
            "Accept: application/json"
        ]
    ]);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpCode !== 200) {
        throw new Exception("API error: HTTP {$httpCode}");
    }

    return json_decode($response, true);
}

// Usage
try {
    $data = getProjectData($project, $apiKey);
    echo "Funding: " . $data['project']['current_funding'] . " €\n";
    echo "Supporters: " . $data['project']['supporter_count'] . "\n";
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

Python (requests)

import requests

API_KEY = 'snx_proj_xxxxxxxx'
PROJECT = 'my-project'

def get_project_data(project, api_key):
    url = f'https://www.startnext.com/myty/api/crowdfunding/project/{project}'
    headers = {
        'Authorization': f'Bearer {api_key}',
        'Accept': 'application/json'
    }

    response = requests.get(url, headers=headers)
    response.raise_for_status()

    return response.json()

# Usage
try:
    data = get_project_data(PROJECT, API_KEY)
    print(f"Funding: {data['project']['current_funding']} €")
    print(f"Supporters: {data['project']['supporter_count']}")
except requests.exceptions.HTTPError as e:
    print(f"Error: {e}")

Usage Examples

Display Funding Progress on Your Own Website

Show the current funding status of your project on your website:


<div id="funding-widget">
    <div class="progress-bar">
        <div class="progress" id="progress"></div>
    </div>
    <p id="funding-status"></p>
    <p><span id="supporters">0</span> supporters</p>
</div>

<script>
    async function updateWidget() {
        const response = await fetch(
            'https://www.startnext.com/myty/api/crowdfunding/project/my-project',
            {headers: {'Authorization': 'Bearer snx_proj_xxx'}}
        );
        const {project} = await response.json();

        // project_type_all_or_nothing: true  = funding goal must be reached
        // project_type_all_or_nothing: false = successful from the first support
        if (project.project_type_all_or_nothing && project.funding_threshold) {
            const percentage = (project.current_funding / project.funding_threshold) * 100;
            document.getElementById('progress').style.width = `${Math.min(percentage, 100)}%`;
            document.getElementById('funding-status').innerHTML =
                `<span>${project.current_funding.toFixed(2)}</span> ${project.currency} of ` +
                `<span>${project.funding_threshold.toFixed(2)}</span> ${project.currency} funded`;
        } else {
            // Project without funding goal
            document.getElementById('progress').style.width = '100%';
            document.getElementById('funding-status').innerHTML =
                `<span>${project.current_funding.toFixed(2)}</span> ${project.currency} raised`;
        }
        document.getElementById('supporters').textContent = project.supporter_count;
    }

    updateWidget();
    setInterval(updateWidget, 60000); // Refresh every 60 seconds
</script>

Display Rewards in Your Own Shop

async function loadIncentives() {
    const response = await fetch(
        'https://www.startnext.com/myty/api/crowdfunding/project/my-project/incentives',
        {headers: {'Authorization': 'Bearer snx_proj_xxx'}}
    );
    const {data} = await response.json();

    const container = document.getElementById('incentives');

    data.forEach(incentive => {
        const card = document.createElement('div');
        card.className = 'incentive-card';
        card.innerHTML = `
            <h3>${incentive.title}</h3>
            <p>${incentive.description}</p>
            <p class="price">${incentive.price.toFixed(2)} €</p>
            <p class="stock">${incentive.quantity_total === 0
                ? 'unlimited'
                : `${incentive.quantity_remaining} remaining`}</p>
            <a href="https://www.startnext.com/my-project/support" class="button">
                Support now
            </a>
        `;
        container.appendChild(card);
    });
}