Hosting API

There are two main ways to upload or ingest files into Ittybit with varying use-cases:

  • Simple Uploads - suitable for most files or scenarios.

  • Resumable Uploads - best for large files or if you suspect you have an unstable connection.

Simple Uploads

const API_KEY = "YOUR API KEY HERE"
const URL = "YOUR FILE URL HERE"
const response = await fetch(`https://api.ittybit.com/files`, {
  headers: {
    Method: "POST",
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
    body: JSON.stringify({
      url: ${URL}
    }),
  },
});
curl -X POST "https://api.ittybit.com/files"
-H "Authorization: Bearer YOUR_ITTYBIT_API_KEY"
-H "Content-Type: application/json"
-d '{
  "url": "YOUR_URL_HERE",
  }
'

Via the /files endpoint

The simplest way to ingest with a publicly accessible URL.

Copy the following code snippet. Make a POST request and replace the placeholders in the first two lines with your API key and a publicly accessible URL of the media file to be ingested.

This method takes a file from a publicly accessible URL (plus your API key) and ingests the file into Ittybit.

Upload Sessions

Uploads are short-lived sessions that allow you or your users to create new Media Items with a direct file upload from your client to our servers.

const BASE_URL = "https://api.ittybit.com";
const response = await fetch(`https://api.ittybit.com/uploads`, {
  headers: {
    method: "POST",
    Authorization: `Bearer ${ITTYBIT_API_KEY}`,
  },
});
const BASE_URL = "https://api.ittybit.com";
const FILESIZE_LIMIT = 100*1024*1024; // 100MB
const response = await fetch(`https://api.ittybit.com/uploads`, {
  headers: {
    method: "POST",
    Authorization: `Bearer ${ITTYBIT_API_KEY}`,
    "Content-Type": "application/json",
  }
  body: JSON.stringify({
      resumable: true,
      filename: "usr_abcd/profile/video",
      metadata: {
        filename: "original-filename.mp4",
        user_id: "usr_abcd",
      },
    }),
});

1. Create an Upload Session

First, make a POST request to create a new upload session.

This will return an upload URL, that you can then use to upload your file directly from the client to our servers.

You should do this initial request from your server to keep your API keys secret.

{
  "meta":{
    "id": "req_abcdefgh1234",
    "org_id": "org_abcdefgh1234",
    "project_id": "prj_abcdefgh1234",
    "method": "POST",
    "host": "https://api.ittybit.com",
    "path": "/uploads",
    "version": "2024-08-21",
    "status": 200,
    "type": "object"
  },
  "data": {
    "url": "https://yourdomain.com/store/single?key=aWQ9QU...aWQ9QU==",
  },
  "links": {}
}
{
  "meta":{
    "id": "req_abcdefgh1234",
    "org_id": "org_abcdefgh1234",
    "project_id": "prj_abcdefgh1234",
    "method": "POST",
    "host": "https://api.ittybit.com",
    "path": "/uploads",
    "version": "2024-08-21",
    "status": 200,
    "type": "object"
  },
  "data": {
    "url": "https://yourdomain.com/store/resumable?key=aWQ9QU...aWQ9QU==",
    "conditions": [
        { "prop": "kind", "operator": "==", "value": "video" },
        { "prop": "filesize", "operator": "<=", "value": 104857600 },
      ],
    "folder": "usr_abcd",
    "filename": "profile.mp4",
    "metadata": {
      "filename": "original-filename.mp4",
      "user_id": "usr_abcd",
    }
  },
  "links": {}
}

The returned upload url will look something like this: https://yourdomain.com/store/single?key=aWQ9QU...

Endpoint

POST /uploads

Body Parameters

  • resumable boolean

    Whether to initiate a resumable upload session. If this is not included you will create a simple upload session. Default is false.

  • conditions object

    A set of conditions that the media must meet to be accepted. See below for more details.

  • alias string

    A unique alias for the media. This will be used to generate the media URL. It should be unique to your project. It can contain letters, numbers, and hyphens. If not provided, we will use the default URL in the format /:media_id/original.

  • metadata object

    A set of key-value pairs to store with the media. This can be used to store any additional information you need to associate with the media. For example, you might want to store the original filename, the user who uploaded the media, or any other data that is relevant to your project. Valid values are strings, numbers, booleans, and null. Nested objects are not supported.


2. Upload the File

a. Simple Upload Method using an input element

const { data } = await response.json();
const file = document.getElementById("file").files[0];
const result = await fetch(data.url, {
  headers: {
    method: "POST",
    "Content-Type": "multipart/form-data"
  }
  body: new FormData({
      file: file,
  }),
});

In a Simple Upload the file is passed to our server in one POST request.

The Content-Type is multipart/form-data which allows you to send a File directly.

Your upload URL is already authorized to add one file to your project, so no additional Authorization headers are needed. You should not pass your Ittybit API Key to the client.

The only body parameter is the file itself. You can access the file from a file input element using document.getElementById("file").files[0] (assuming the input has id="file").

b. Resumable Upload Method

const { data } = await response.json();
const url = data.url;
const CHUNK_SIZE = 1024 * 1024 * 10; // #B
const file = document.getElementById("file").files[0];
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
async function readFileAsArrayBuffer(file) {
  return new Promise((resolve, reject) => {
    let reader = new FileReader();
    reader.onload = event => resolve(event.target.result);
    reader.onerror = error => reject(error);
    reader.readAsArrayBuffer(file);
  });
}
async function uploadFile({ file, url }) {
  let parts = [];
  let arrayBuffer = await readFileAsArrayBuffer(file);
  for (let i = 0; i < totalChunks; i++) {
    let start = i * CHUNK_SIZE;
    let end = Math.min(file.size, start + CHUNK_SIZE);
    let chunk = arrayBuffer.slice(start, end); // #A
    // #C
    let response = await fetch(url, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/octet-stream', // #D
        'X-Upload-Index': i, // #E
      },
      body: chunk
    });
    if (!response.ok) {
        throw new Error('Upload failed');
    }
    let { data } = await response.json();
    parts.push(data); // #F
    console.log(`Chunk ${i + 1} of ${totalChunks} uploaded`);
  }
  // #G
  let response = await fetch(url, {
    method: 'POST'
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ parts })
  });
  if (!response.ok) {
      throw new Error('Upload completion failed');
  }
  console.log('Upload complete');
  let result = await response.json();
  return result;
}
const result = await uploadFile({ file, url });

In a Resumable Upload the file is split up and passed to our server in multiple smaller chunks.

This allows you to upload much larger files more robustly, to quickly restart an upload if it is interrupted (without aborting the whole upload and starting again), and even to manually pause and resume uploads.

The file size limit is 1GB per file, but you can contact us to increase this limit (upto 5TB per file).

First, you need to split the file. You can do this using the FileReader API to read the file as an ArrayBuffer and then slice it into chunks (#A). In our example we use 10MB chunks (#B).

Then, we loop through the chunks and send each one to the upload url in a PUT request (#C).

As we are sending bytes directly in the body we will set the Content-Type header to application/octet-stream (#D). We also pass a custom X-Upload-Index header containing the index of the chunk (#E). This allows our server to reassemble the file in the correct order even if the chunks arrive out of order due to network issues.

Each PUT request will return a data object containing a PartObject which looks like this:

{
  index: 0,
  etag: "5aaf2....",
}

We store these PartObjects in an array (#F) and send them to the server in a final POST request to complete the upload (#G).

The POST request will return a data object which contains the media details.

Note 1 This is a simplified example. In a real-world app we would handle errors, retries, and display upload progress. There is a guide coming very soon!

Note 2 If you use React on your frontend we can provide an <Upload /> component that handles all of this for you. Contact us for more information. If you are using a different frontend framework, please get in touch and we'll be happy to work with you to create a similar solution.


3. Optional: Save Media Details

{
  "meta":{
    "id": "req_abcdefgh1234",
    "org_id": "org_abcdefgh1234",
    "project_id": "prj_abcdefgh1234",
    "type": "object",
    "status": 200,
  },
  "data": {
    "id": "med_abcdefgh1234",
    "object": "media",
    "kind": "video",
    "width": 1920,
    "height": 1080,
    "duration": 60,
    "filesize": 12345678,
    "alias": "usr_abcd/profile/video",
    "url": "https://yourdomain.com/usr_abcd/profile/video/",
    "sources": [
      {
        "id": "src_abcdefgh1234",
        "object": "source",
        "label": "original",
        // ... Source object props
        // https://ittybit.com/docs/api/sources
        "url": "https://yourdomain.com/med_abcdefgh1234/original",
      }
    ],
    "intelligence": [],
    "metadata": {
      "filename": "original-filename.mp4",
      "user_id": "usr_abcd",
    },
    "created": "2024-03-30T13:00:00Z",
    "updated": "2024-03-30T13:00:00Z",
    "status": "active"
  },
  "links": {
    "self": "https://api.ittybit.com/media/med_abcdefgh1234",
    "parent": "https://api.ittybit.com/media",
  }
}

After the upload is complete, you can save the media details – or a subset such as the media's id and url – to your database or CMS.

A successful response will contain a full Media object, and the Source object for the original file.

We automatically analyse each upload and will include some file properties for the new Source:

  • kind string

    The type of media. Possible values are image, video, or audio.

  • type string

    The MIME type of the media. For example, image/jpeg, video/mp4, audio/mpeg.

  • width integer

    The width of the media in pixels. For audio files this will be null.

  • height integer

    The height of the media in pixels. For audio files this will be null.

  • duration number

    The duration of the media in seconds. Only available for videos and audio files.

  • fps number

    The frames per second of the media. Only available for videos and animated images.

  • filesize integer

    The size of the media in bytes.

  • bitrate integer

    The bitrate of the media in kbps. For images this will be null.


Upload Conditions

You can set conditions on your upload to ensure that only media that meets your requirements is accepted.

const FILESIZE_LIMIT = 100*1024*1024; // 100MB
const MAX_LENGTH = 1920; // px
const MAX_DURATION = 5 * 60; // 5 minutes
const conditions = {
  operator: "&&",
  rules: [
    { key: "kind", operator: "==", value: "video" },
    { key: "filesize", operator: "<=", value: FILESIZE_LIMIT },
    { key: "width", operator: "<=", value: MAX_LENGTH },
    { key: "height", operator: "<=", value: MAX_LENGTH },
    { key: "duration", operator: "<=", value: MAX_DURATION }
  ],
}

Body Parameters

  • operator string

    The operator to use to combine the rules. Possible values are && (AND) / || (OR).

  • rules array

    An array of rules to apply to the media. Each rule is an object with the following properties:

    • key string

      The key of the media property to check. Possible values are kind, mimetype, filesize, width, height, duration.

    • operator string

      The operator to use to compare the value. Possible values are ==, !=, >, <, >=, <=.

    • value varies

      The value to compare the media property to. The type of this value depends on the key and operator.

Note You will still be charged for an upload (1x the filesize against your monthly usage limit) even if the media is rejected on the server-side. So it's best to also validate the media on the client-side before starting the upload. Server-side conditions give you an extra layer of security and additional options that aren't possible on the client-side.

Uploading via the web app

Alternatively, you can upload a media file from your local machine to Ittybit using the web app.

1. Navigate to the web app

2. Go the Media tab.

3. Select the Uploads menu on your lower-left hand side

  • Click "Select files" and navigate to the file you wish to upload.
  • If you click through to Media, you may also drag and drop the file you wish to upload directly onto the browser.
  • Note: For more information on supported files, please check Supported files

4. Wait for your upload to finish 

  • The upload will have begun once you see a new thumbnail of your file represented on the upload menu or under the media section. A bar will begin loading to indicate the progress of the upload. -

  • Please note that your upload will be interrupted if you close the tab or navigate away. For resumable sessions, please check [Resumable upload sessions] - If your file may take longer than 1 hour to finish uploading, please use the [Resumable upload] feature. The web app upload times out at the 1 hour mark.

  • Once completed, the upload menu will indicate with a "READY" status and 100% on the progress bar.

Please check back soon or get in touch to ask any questions or book a free pairing session.