Skip to main content

Overview

Raily provides multiple ways to import your content, from single-item API calls to bulk imports from your existing CMS. This guide covers all ingestion methods and best practices.

Ingestion Methods

API

Direct API calls for programmatic control

Bulk Import

CSV/JSON uploads for large datasets

CMS Sync

Automatic sync with your CMS

Single Content via API

The simplest way to add content is through the API:
const content = await raily.content.create({
  externalId: "blog-post-123",
  title: "Getting Started with AI",
  type: "article",
  source: "https://blog.example.com/ai-getting-started",
  metadata: {
    author: "Sarah Johnson",
    publishedAt: "2024-01-15T10:00:00Z",
    tags: ["ai", "tutorial", "beginner"],
    wordCount: 1500,
    readingTime: 6
  }
});

Content Fields Reference

FieldRequiredDescription
externalIdYesYour unique identifier for this content
titleYesHuman-readable title
typeYesContent type: article, report, image, dataset, video
sourceYesURL or path to the original content
metadataNoCustom key-value pairs for your data
policyIdNoAccess policy to apply
tagsNoArray of tags for categorization

Bulk Import

For large content libraries, use bulk import to add thousands of items at once.

JSON Format

{
  "items": [
    {
      "externalId": "article-001",
      "title": "Introduction to Machine Learning",
      "type": "article",
      "source": "https://example.com/ml-intro",
      "metadata": {
        "author": "Dr. Emily Chen",
        "category": "Education"
      }
    },
    {
      "externalId": "article-002",
      "title": "Deep Learning Fundamentals",
      "type": "article",
      "source": "https://example.com/dl-fundamentals",
      "metadata": {
        "author": "Dr. Emily Chen",
        "category": "Education"
      }
    }
  ]
}

CSV Format

externalId,title,type,source,author,category
article-001,Introduction to Machine Learning,article,https://example.com/ml-intro,Dr. Emily Chen,Education
article-002,Deep Learning Fundamentals,article,https://example.com/dl-fundamentals,Dr. Emily Chen,Education

Bulk Import via API

// From JSON array
const result = await raily.content.bulkCreate({
  items: [
    {
      externalId: "article-001",
      title: "Introduction to Machine Learning",
      type: "article",
      source: "https://example.com/ml-intro"
    },
    {
      externalId: "article-002",
      title: "Deep Learning Fundamentals",
      type: "article",
      source: "https://example.com/dl-fundamentals"
    }
    // ... up to 1000 items per request
  ],
  options: {
    skipDuplicates: true,  // Skip items with existing externalId
    defaultPolicy: "pol_abc123"  // Apply this policy to all items
  }
});

console.log(`Imported: ${result.created}`);
console.log(`Skipped: ${result.skipped}`);
console.log(`Errors: ${result.errors.length}`);

Upload from File

import fs from 'fs';

// Upload from JSON file
const data = JSON.parse(fs.readFileSync('content.json', 'utf8'));
const result = await raily.content.bulkCreate({
  items: data.items,
  options: { skipDuplicates: true }
});

// Or use the upload endpoint for CSV
const csvFile = fs.createReadStream('content.csv');
const uploadResult = await raily.content.upload({
  file: csvFile,
  format: 'csv',
  options: {
    skipDuplicates: true,
    columnMapping: {
      'Article ID': 'externalId',
      'Title': 'title',
      'URL': 'source'
    }
  }
});

CMS Integrations

Raily integrates with popular CMS platforms for automatic content sync.

WordPress

1

Install the Plugin

Download the Raily WordPress plugin from your dashboard or search “Raily” in the WordPress plugin directory.
2

Configure API Key

Go to Settings > Raily and enter your API key.
3

Select Content Types

Choose which post types to sync (posts, pages, custom types).
4

Enable Auto-Sync

Toggle on automatic sync to push content changes in real-time.
// WordPress filter to customize content metadata
add_filter('raily_content_metadata', function($metadata, $post) {
    return array_merge($metadata, [
        'premium' => get_post_meta($post->ID, 'is_premium', true),
        'series' => get_the_terms($post->ID, 'series'),
    ]);
}, 10, 2);

Contentful

// Contentful webhook handler
import { createClient } from 'contentful';
import Raily from '@raily/sdk';

const contentful = createClient({
  space: process.env.CONTENTFUL_SPACE_ID,
  accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});

const raily = new Raily({
  apiKey: process.env.RAILY_SECRET_KEY,
});

// Sync a Contentful entry to Raily
async function syncEntry(entryId) {
  const entry = await contentful.getEntry(entryId);

  await raily.content.upsert({
    externalId: entry.sys.id,
    title: entry.fields.title,
    type: 'article',
    source: `https://yoursite.com/articles/${entry.fields.slug}`,
    metadata: {
      author: entry.fields.author?.fields?.name,
      publishedAt: entry.sys.createdAt,
      updatedAt: entry.sys.updatedAt,
    }
  });
}

Strapi

// Strapi lifecycle hook
module.exports = {
  async afterCreate(event) {
    const { result } = event;
    await syncToRaily(result);
  },

  async afterUpdate(event) {
    const { result } = event;
    await syncToRaily(result);
  },
};

async function syncToRaily(article) {
  await raily.content.upsert({
    externalId: `strapi-${article.id}`,
    title: article.title,
    type: 'article',
    source: `https://yoursite.com/blog/${article.slug}`,
    metadata: {
      author: article.author?.name,
      category: article.category?.name,
    }
  });
}

Content Updates

Keep your content in sync with these update strategies.

Single Update

await raily.content.update('cnt_abc123', {
  title: "Updated Title",
  metadata: {
    lastModified: new Date().toISOString(),
    version: 2
  }
});

Upsert (Create or Update)

// Creates if doesn't exist, updates if it does
await raily.content.upsert({
  externalId: "article-123",
  title: "My Article",
  type: "article",
  source: "https://example.com/article"
});

Bulk Update

await raily.content.bulkUpdate({
  filter: {
    type: "article",
    "metadata.category": "News"
  },
  update: {
    policyId: "pol_news_policy"
  }
});

Content Validation

Raily validates content before ingestion. Here are common validation errors:
Error: source must be a valid URLSolution: Ensure the source field contains a properly formatted URL including the protocol (https://).
// Wrong
source: "example.com/article"

// Correct
source: "https://example.com/article"
Error: Content with externalId already existsSolution: Use upsert instead of create, or use a unique external ID.
// Use upsert for create-or-update behavior
await raily.content.upsert({
  externalId: "article-123",
  // ...
});
Error: type must be one of: article, report, image, dataset, videoSolution: Use one of the supported content types or contact support for custom types.

Best Practices

Use External IDs

Always set meaningful externalId values that match your internal system. This makes syncing and debugging much easier.

Batch Operations

Use bulk endpoints for large imports. Single-item calls are rate-limited to 100/minute; bulk can handle 1000 items per request.

Rich Metadata

Include as much metadata as possible. This enables better filtering, analytics, and policy rules later.

Idempotent Imports

Use upsert with skipDuplicates for safe, repeatable import processes.

Next Steps