Seedance 2.5 is live — 30-second cinematic video with native audio & real-person references
Сколько стоит ежедневное видео AI? Бюджет на 30 дней
2026/09/03

Сколько стоит ежедневное видео AI? Бюджет на 30 дней

Рассчитайте 30-дневный бюджет на вертикальное AI-видео: повторы, лимиты бесплатных кредитов, безопасный опрос API, хранение и ручная проверка.

Для протестированной конфигурации ежедневное видео стоит $2.52 за генерацию на 30 дней. Каждая попытка — пятисекундный клип 480p, 9:16 с seedance-2.0-mini. Один тест обошёлся 84 кредитами reAPI, или $0.084, завершился за 107.2 секунды и вернул один URL видео.[6]

Это простой ответ, но ещё не рабочий бюджет. Завершённая задача API — не обязательно готовый клип. Если ревью отклонит половину попыток, бюджет вырастет до $5.04 в месяц. Хранилище, планирование, редактирование и ревью находятся вне этого.

Это руководство рассчитывает честное число и строит пример с одним рабочим, который останавливается для ручного согласования вместо угадывания повтора неопределённого POST.

TL;DR

  • Одна задача Seedance 2.0 Mini на 5 секунд, 480p, 9:16 потребила 84 кредита = $0.084. Тридцать таких попыток обошлись бы $2.52.
  • Бюджетируйте по завершённым попыткам на принятый клип, а не по запланированным постам. При 1.5 попыток на клип месячный бюджет $3.78; при двух — $5.04.
  • Новые аккаунты могут получить рекламные кредиты, но публичная страница цен не обещает фиксированное количество или ежедневный сброс.[3] Рассматривайте их как протоип, а не ежемесячную вместимость.
  • Сохраняйте ID задачи сразу. Каждый новый POST создаёт отдельную задачу и резервирование; каждая завершённая задача рассчитывает собственное использование. Idempotency-Key — это трассировочное значение, не дедублирование видеозапросов.[4]
  • Опрашивайте с GET каждые пять секунд, архивируйте результат в подконтрольное хранилище и публикуйте за ручным ревью.[5]
  • 107.2 секунды — это одиночный тест, а не утверждение про среднюю скорость, надёжность или качество.

Вопрос про ежедневное видео

Нужен ли реальный REST API, создающий один-два коротких вертикальных клипа в день, возвращающий публичный HTTPS URL и встраивающийся в поток публикации? Для прототипа 9:16 в 4–5 секунд стоимость — только половина. Восстановление важно когда прототип становится расписанием: нужно устойчивое состояние и политика повторов перед умным промптом.

Один забег на 107 секунд

Тест использовал самую дешёвую конфигурацию reAPI, принимающую текст и поддерживающую 9:16 с 4–5 секундами: Seedance 2.0 Mini на 480p.[1] Модель поддерживает 480p или 720p, 9:16 и 4–15 секунд.[2]

ПараметрЗначение
Дата3 сентября 2026
ID задачиtask_01a06547163473ecb7dab4478ee1abaa
Модельseedance-2.0-mini
Запрос480p, 9:16, 5 сек
АудиоВыкл
Водяной знакВыкл
Отправок1
СтатусЗавершено
Время107.2 сек
Использование84 кредита / $0.084
Результат1 URL видео

Этот результат: 5 сек, 480p, 9:16, аудио и водяной знак отключены. Проверены завершение и платёж; качество не оценивалось.

Ставка 480p составляет $0.016625 за секунду.[1] reAPI: 1 кредит = $0.001, округление вверх.[2] Расчёт:

ceil($0.016625 × 5 × 1,000)
= ceil(83.125)
= 84
= $0.084

POST резервирует кредиты для задачи. Рассчёт когда задача терминальна: читайте status с usage.credits, не считайте фиксацию финалом. Seedance 2.0 Mini возвращает неудачу по политике.[2][5]

Этот результат подтверждает путь, статус, URL и плату. Не устанавливает типичное время, успех, принятие или качество.

Загрузить запись. Содержит параметры, ID, время, статус и кредиты. Временный URL пропущен; запись указывает на постоянный CDN.

Цены меняются. Проверьте страницу Seedance 2.0 Mini перед бюджетом.

30-дневный бюджет

Минимальный расчёт:

30 × 84 = 2 520 кредитов = $2.52

Один клип в день. Полезная формула:

ежемесячная стоимость = дни × видео/день × попытки/видео × стоимость/попытка

За один клип в день:

Попыток/видеоВсего попытокМесячный бюджет
1.030$2.52
1.545$3.78
2.060$5.04
3.090$7.56

"1.5" — среднее: 15 первых попыток, 15 замен. Считайте отклонённые за мотион, композицию, или расхождение.

Разрешение и время меняют цену. Цифры — текущие ставки; только 480p 5-сек был тестом:

КонфигурацияКредитовСтоимость30 раз
480p, 4 сек67$0.067$2.01
480p, 5 сек84$0.084$2.52
720p, 4 сек144$0.144$4.32
720p, 5 сек180$0.180$5.40

Это генерация. Исключает промпты, активы, редактирование, музыку, хранилище, хостинг, платежи и время ревью.

Свободные кредиты недостаточны

Свободные кредиты доказывают auth, поля, опрос, загрузку. Не гарантируют без суммы и сброса.

Новые аккаунты получают рекламные кредиты; показаны при регистрации. Публичная цена не обещает фиксацию или сброс.[3] Непригодны как месячная вместимость. То же на приложения: без API доступа, лимитов, сброса — UI кредиты не бюджет.

Тестируйте узкий путь. Бюджетируйте платное и рассматривайте свободные как временное. Это деле после запуска, не на демо.

Перед автоматизацией

Подготовьте:

  • Аккаунт reAPI и серверный ключ. Не в браузер, репо или скачиваемый рабочий.
  • Промпт на короткий кадр. 5 сек — не для истории.
  • Планировщик: cron, GitHub Actions, n8n, очередь.
  • Место для ежедневного ключа и ID задачи.
  • Хранилище для готовых файлов.
  • Человек для ревью перед публикацией.

Примеры — UTC. Если локальное время, считайте дату в том поясе. Около полуночи может быть неправильный ключ.

Отправить видео 9:16 cURL

Установите ключ:

export REAPI_API_KEY='replace-with-your-server-side-key'

Отправьте и сохраните:

curl --fail-with-body --silent --show-error \
  --request POST 'https://reapi.ai/api/v1/videos/generations' \
  --header "Authorization: Bearer $REAPI_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
    "model": "seedance-2.0-mini",
    "prompt": "A handmade ceramic cup beside an open notebook, soft morning window light, one slow push-in shot, natural motion, no text",
    "resolution": "480p",
    "aspect_ratio": "9:16",
    "duration": 5,
    "generate_audio": false,
    "watermark": false
  }' \
  --output submit.json

Откройте submit.json и скопируйте id. Если jq:

TASK_ID="$(jq -r '.id' submit.json)"
test -n "$TASK_ID" && test "$TASK_ID" != 'null' || exit 1
printf '%s\n' "$TASK_ID" > task-id.txt

Опрос GET не тратит кредиты:[5]

curl --fail-with-body --silent --show-error \
  --header "Authorization: Bearer $REAPI_API_KEY" \
  "https://reapi.ai/api/v1/tasks/$TASK_ID" \
  --output task.json
jq '{id, status, usage, output, error}' task.json

Повторяйте через 5 сек пока processing. Финалы: completed или failed. Не повторяйте POST если видео ещё нет.

Безопасный рабочий в Node.js

cURL доказывает поля. Скрипт ниже — один рабочий, не-более-один раз, не очередь. Сохраняет намерение перед POST, записывает ID как прибыл ответ, ограничивает вызовы, архивирует, останавливается на неопределённость. Node.js 20+, без пакетов. Не две копии на файл.

Сохраните daily-video.mjs:

import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
import { join } from 'node:path';

const API_BASE = 'https://reapi.ai/api/v1';
const STORE_PATH = './daily-video-jobs.json';
const OUTPUT_DIR = './daily-video-output';
const POLL_MS = 5_000;
const POLL_LIMIT_MS = 30 * 60 * 1_000;
const POST_TIMEOUT_MS = 30_000;
const GET_TIMEOUT_MS = 20_000;
const DOWNLOAD_TIMEOUT_MS = 120_000;
const apiKey = process.env.REAPI_API_KEY;

if (!apiKey) {
  throw new Error('Set REAPI_API_KEY in the server environment.');
}

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

function retryAfterMs(value) {
  if (!value) return POLL_MS;
  const seconds = Number(value);
  if (Number.isFinite(seconds)) return Math.max(seconds * 1_000, 0);
  const date = Date.parse(value);
  return Number.isNaN(date) ? POLL_MS : Math.max(date - Date.now(), 0);
}

async function loadJobs() {
  try {
    return JSON.parse(await readFile(STORE_PATH, 'utf8'));
  } catch (error) {
    if (error.code === 'ENOENT') return {};
    throw error;
  }
}

async function saveJobs(jobs) {
  const temporaryPath = `${STORE_PATH}.tmp`;
  await writeFile(temporaryPath, `${JSON.stringify(jobs, null, 2)}\n`);
  await rename(temporaryPath, STORE_PATH);
}

async function requestJson(url, options) {
  const response = await fetch(url, options);
  const body = await response.text();
  let data;

  try {
    data = JSON.parse(body);
  } catch {
    data = { raw: body };
  }

  if (!response.ok) {
    const error = new Error(`HTTP ${response.status}: ${body.slice(0, 300)}`);
    error.status = response.status;
    error.retryAfterMs = retryAfterMs(response.headers.get('retry-after'));
    error.requestId = data?.error?.request_id ?? null;
    throw error;
  }

  return data;
}

async function archiveVideo(url, jobKey) {
  const response = await fetch(url, {
    signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS),
  });
  if (!response.ok) {
    throw new Error(`Video download failed with HTTP ${response.status}.`);
  }

  await mkdir(OUTPUT_DIR, { recursive: true });
  const safeName = jobKey.replace(/[^a-z0-9-]/gi, '_');
  const outputPath = join(OUTPUT_DIR, `${safeName}.mp4`);
  const bytes = Buffer.from(await response.arrayBuffer());
  await writeFile(outputPath, bytes);
  return outputPath;
}

async function main() {
  const utcDate = new Date().toISOString().slice(0, 10);
  const jobKey = `daily-short:${utcDate}`;
  const jobs = await loadJobs();

  if (!jobs[jobKey]) {
    // Persist intent before POST. A crash after this point must not cause an
    // automatic second submission on the next scheduler run.
    // Save completion before downloading so storage trouble cannot trigger POST.
    jobs[jobKey] = {
      state: 'submitting',
      createdAt: new Date().toISOString(),
      idempotencyKey: jobKey,
    };
    await saveJobs(jobs);

    try {
      const task = await requestJson(`${API_BASE}/videos/generations`, {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${apiKey}`,
          'Content-Type': 'application/json',
          // Trace-only on reAPI. This header does not deduplicate the POST.
          'Idempotency-Key': jobKey,
        },
        signal: AbortSignal.timeout(POST_TIMEOUT_MS),
        body: JSON.stringify({
          model: 'seedance-2.0-mini',
          prompt:
            'A handmade ceramic cup beside an open notebook, soft morning ' +
            'window light, one slow push-in shot, natural motion, no text',
          resolution: '480p',
          aspect_ratio: '9:16',
          duration: 5,
          generate_audio: false,
          watermark: false,
        }),
      });

      if (!task.id) throw new Error('Submission returned no task ID.');

      jobs[jobKey] = {
        ...jobs[jobKey],
        state: 'processing',
        taskId: task.id,
        submittedAt: new Date().toISOString(),
      };
      await saveJobs(jobs);
    } catch (error) {
      const receivedHttpResponse = Number.isInteger(error.status);
      jobs[jobKey] = {
        ...jobs[jobKey],
        state: receivedHttpResponse ? 'submit_rejected' : 'submit_uncertain',
        error: error instanceof Error ? error.message : String(error),
        requestId: error.requestId ?? null,
      };
      await saveJobs(jobs);
      throw new Error(
        receivedHttpResponse
          ? `Submission for ${jobKey} was rejected. Review the HTTP error ` +
              'before creating a separately counted retry.'
          : `Submission for ${jobKey} is uncertain. Use ${jobKey} and the ` +
              'saved request ID, if present, to inspect logs or the dashboard. ' +
              'Do not submit again until a person reconciles the result.',
      );
    }
  }

  let job = jobs[jobKey];
  if (job.state === 'pending_review' || job.state === 'approved') {
    console.log(`${jobKey} already has an archived output: ${job.archivePath}`);
    return;
  }
  if (job.state === 'completed_no_output') {
    throw new Error(
      `${jobKey} completed without a video URL. Inspect ${job.taskId}; do not POST again.`,
    );
  }
  if (job.state === 'failed') {
    throw new Error(
      `${jobKey} failed. Review its settled usage before creating a new retry key.`,
    );
  }
  if (!job.taskId) {
    throw new Error(
      `${jobKey} has no task ID. Resolve ${job.state} manually; do not resubmit.`,
    );
  }

  if (!job.outputUrl) {
    const deadline = Date.now() + POLL_LIMIT_MS;
    let task;

    while (Date.now() < deadline) {
      try {
        task = await requestJson(`${API_BASE}/tasks/${job.taskId}`, {
          headers: { Authorization: `Bearer ${apiKey}` },
          signal: AbortSignal.timeout(GET_TIMEOUT_MS),
        });
      } catch (error) {
        const remainingMs = Math.max(deadline - Date.now(), 0);
        if (error.status === 429) {
          const waitMs = Math.min(error.retryAfterMs ?? POLL_MS, remainingMs);
          console.warn(`Rate limited; waiting ${waitMs} ms before another GET.`);
          if (waitMs > 0) await sleep(waitMs);
          continue;
        }
        const networkError = !Number.isInteger(error.status);
        const serverError = error.status >= 500 && error.status < 600;
        if (!networkError && !serverError) {
          throw error;
        }
        const waitMs = Math.min(POLL_MS, remainingMs);
        console.warn('Network or server error; retrying the same task GET.');
        if (waitMs > 0) await sleep(waitMs);
        continue;
      }

      if (task.status === 'completed' || task.status === 'failed') break;
      if (task.status !== 'processing') {
        throw new Error(`Unexpected task status: ${task.status}`);
      }
      await sleep(Math.min(POLL_MS, Math.max(deadline - Date.now(), 0)));
    }

    if (!task || (task.status !== 'completed' && task.status !== 'failed')) {
      jobs[jobKey] = { ...job, state: 'poll_timeout' };
      await saveJobs(jobs);
      throw new Error(
        'Polling timed out. The task may still be running; do not POST again.',
      );
    }

    if (task.status === 'failed') {
      jobs[jobKey] = {
        ...job,
        state: 'failed',
        usageCredits: task.usage?.credits ?? null,
        taskError: task.error ?? null,
      };
      await saveJobs(jobs);
      throw new Error(
        'Generation failed. Review settled usage before scheduling a retry.',
      );
    }

    const outputUrl = task.output?.video_urls?.[0];
    if (!outputUrl) {
      jobs[jobKey] = {
        ...job,
        state: 'completed_no_output',
        usageCredits: task.usage?.credits ?? null,
        completedAt: new Date().toISOString(),
      };
      await saveJobs(jobs);
      throw new Error('Completed task returned no video URL; do not POST again.');
    }

    jobs[jobKey] = {
      ...job,
      state: 'completed',
      outputUrl,
      usageCredits: task.usage?.credits ?? null,
      completedAt: new Date().toISOString(),
    };
    await saveJobs(jobs);
    job = jobs[jobKey];
  }

  try {
    const archivePath = await archiveVideo(job.outputUrl, jobKey);
    jobs[jobKey] = {
      ...job,
      state: 'pending_review',
      archivePath,
      archiveError: null,
    };
    await saveJobs(jobs);
    console.log(`Saved ${archivePath}. Review it before publishing.`);
  } catch (error) {
    jobs[jobKey] = {
      ...job,
      state: 'archive_failed',
      archiveError: error instanceof Error ? error.message : String(error),
    };
    await saveJobs(jobs);
    throw new Error(
      'Archiving failed. The task remains completed; rerun to retry only the download.',
    );
  }
}

await main();

Запустите:

node daily-video.mjs

JSON перезапускаем на одной машине, но не мульти-рабочая блокировка. В прода вставьте строку БД с уникальным job_key, пусть только выигравший рабочий переместит с scheduled на submitting. БД блокировка предотвращает две одновременные отправки. Не может сделать запись и внешний API вызов атомарными.

После краша

Безопасное действие зависит от последнего сохранённого состояния:

СостояниеЧто знаетДействие
Нет строкиНамерение не сохранилось, POST не былНачните нормально
submitting/submit_uncertain, нет IDPOST может не начаться или принялсяСогласуйте вручную; не авто POST
submit_rejectedПришёл не-2xxПроверьте ошибку, новая повторная попытка
processing/poll_timeout, есть IDЗадача существуетВозобновите GET опрос
failedТерминал ошибка, расчётПроверьте usage.credits; новый ключ после решения
completed/archive_failed, есть URLГенерация готоваПовторите только загрузку
completed_no_outputЗакончилась без URLОсмотрите; не POST
pending_review/approvedАрхивированВыход

Неопределённость — неизбежный разрыв. Соединение может упасть после принятия POST. Скрипт отправляет ключ как Idempotency-Key для логов, но reAPI только записывает для трассировки.[4] Второй POST создаст другую задачу. Нет честного автоматического точно-один раза.

GET другой. Повтор не создаёт генерацию и не тратит кредиты. Пример ограничивает запросы, стопит 4xx, чтит Retry-After на 429, повторяет сеть/5xx в 30-минутном окне. 5-сек интервал избегает кэша в полёте.[5]

Проверьте перед публикацией

Задача API заканчивается доставкой. Готовый клип может не подходить или упасть на ленте. Проверьте:

  • Предмет, действие, движение камеры;
  • Искажённые объекты, руки, лица, упаковка, логотипы;
  • Нежелаемые слова, выдуманные утверждения;
  • Мерцание, смены сцен, опасные кадры;
  • Права на промпт, материалы, музыку, лица;
  • Требования платформы.

Отметьте approved только после. Если нет, создайте daily-short:2026-09-03:retry-1; не стирайте первую из реестра.

Переместите MP4 перед добавлением в очередь. URL reAPI — место передачи, не библиотека.[5] Сохраняйте локально; используйте S3, R2 или своё на эфемерном хосте.

Что не обещает $2.52

Месячное число — прозрачный сценарий, не гарантия. Предполагает ту же цену, время, разрешение и попыток 30 дней. Не устанавливает:

  • Типичное время из одного теста 107.2 сек;
  • Надёжность или отказ;
  • Принятие визуально;
  • Точные размеры сверх 480p и 9:16;
  • Успешность публикации;
  • Будущие цены или кредиты.

Держите переменные видимыми. Записывайте job_key, task_id, параметры, статус, usage.credits, ревью, URL. Через 30 дней замените число на ваше.

FAQ

Сколько 5-сек видео в день?

Seedance 2.0 Mini: 84 кредита, $0.084 за одну задачу. В день 30 дней $2.52. Проверьте цену перед бюджетом.

Два видео в день?

60 попыток $5.04. Умножьте на ваши попытки/видео. 1.5 = $7.56.

Бесплатный API с суточным сбросом?

Не предполагайте без документированного доступа, суммы, моделей, сброса. reAPI выдаёт рекламные кредиты при регистрации; публичная цена не обещает.[3]

Почему считать попытки, не клипы?

API может готовый клип, но ревью отклонит. Счёт по генерации; календарь по принятым. Оба числа раскрывают реальную стоимость.

Стоят ли неудачи?

Документация: неудачи возвращаются автоматически.[2] Всё равно записывайте финальный статус и usage.credits. Неудача стоит времени и замены.

Безопасно повторить с тем же ключом?

Нет. Каждый POST — отдельная задача и резервирование; каждая рассчитывает свои usage.credits. Idempotency-Key — трассировочное, не дедублирование.[4] Сохраняйте свой ключ и ID, стопите на неопределённость.

Публиковать прямо из URL?

Сначала архивируйте. URL — место передачи, не хранилище. Прямо пропускает ревью. Сохраните, проверьте, передайте своё.[5]

Нормально ли 107.2 сек?

Не знаем. Один тест. Типичное требует много дат, выборки, включая отказы и таймауты.

Заключение

Ответ на "Сколько стоит?" не просто $0.084. $2.52 идеально или $3.78–$7.56 при 1.5–3 попыток/клип. Начните с диапазона, измеряйте свой процент и держите свободные вне плана.

Первый рубеж: один ключ, один ID, безопасный GET, один архив, одобрение. Работает — планирование каждый день легко.

Ссылки

  1. reAPI. Каталог видео и Seedance 2.0 Mini. 3 сентября 2026.
  2. reAPI. Документация Seedance 2.0 Mini. 3 сентября 2026.
  3. reAPI. Цена. 3 сентября 2026.
  4. reAPI. API. 3 сентября 2026.
  5. reAPI. Задачи API. 3 сентября 2026.
  6. reAPI. Запись теста Seedance 2.0 Mini. Задача task_01a06547163473ecb7dab4478ee1abaa, 3 сентября 2026. Один 480p, 9:16, 5-сек запрос без аудио и водяного знака; 84 кредита, 107.2 сек, один URL.

Дальнейшее чтение