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

# Step 2: Discover methods to import historical data

export const MintTable = ({columns = [], rows = [], columnWidths = []}) => {
  const pushTextWithLineBreaks = (parts, text, keyBase) => {
    const segments = String(text).split(/\\n|\n/);
    segments.forEach((segment, idx) => {
      if (segment) {
        parts.push(<span key={`${keyBase}-text-${idx}`}>{segment}</span>);
      }
      if (idx < segments.length - 1) {
        parts.push(<br key={`${keyBase}-br-${idx}`} />);
      }
    });
  };
  const parseMarkdown = text => {
    if (text === null || text === undefined) return "";
    const str = String(text);
    const parts = [];
    let lastIndex = 0;
    const pattern = /(`[^`]+`|\*\*[^*]+\*\*|\*[^*]+\*|\[([^\]]+)\]\(([^)]+)\))/g;
    let match;
    while (true) {
      match = pattern.exec(str);
      if (match === null) {
        break;
      }
      if (match.index > lastIndex) {
        pushTextWithLineBreaks(parts, str.substring(lastIndex, match.index), `before-${lastIndex}`);
      }
      const fullMatch = match[0];
      if (fullMatch.startsWith("`") && fullMatch.endsWith("`")) {
        parts.push(<code key={match.index}>{fullMatch.slice(1, -1)}</code>);
      } else if (fullMatch.startsWith("**") && fullMatch.endsWith("**")) {
        parts.push(<strong key={match.index}>{fullMatch.slice(2, -2)}</strong>);
      } else if (fullMatch.startsWith("*") && fullMatch.endsWith("*")) {
        parts.push(<em key={match.index}>{fullMatch.slice(1, -1)}</em>);
      } else if (fullMatch.startsWith("[")) {
        const linkText = match[2];
        const linkUrl = match[3];
        parts.push(<a key={match.index} href={linkUrl} className="text-black-600 dark:text-black-400">
            {linkText}
          </a>);
      }
      lastIndex = pattern.lastIndex;
    }
    if (lastIndex < str.length) {
      pushTextWithLineBreaks(parts, str.substring(lastIndex), `tail-${lastIndex}`);
    }
    if (parts.length > 0) {
      return parts;
    }
    const plainParts = [];
    pushTextWithLineBreaks(plainParts, str, "plain");
    return plainParts.length ? plainParts : str;
  };
  const safeColumns = Array.isArray(columns) ? columns : [];
  const safeRows = Array.isArray(rows) ? rows : [];
  const safeColumnWidths = Array.isArray(columnWidths) ? columnWidths : [];
  const hasColumnWidths = safeColumnWidths.some(w => w !== null && w !== undefined && w !== "");
  const toCssWidth = width => typeof width === "number" ? `${width}px` : String(width);
  const getColumnStyle = idx => {
    const rawWidth = safeColumnWidths[idx];
    if (rawWidth === null || rawWidth === undefined || rawWidth === "") {
      return undefined;
    }
    const width = toCssWidth(rawWidth);
    return {
      width,
      minWidth: width
    };
  };
  const containerStyle = hasColumnWidths ? undefined : {
    overflowX: "auto"
  };
  const tableStyle = hasColumnWidths ? {
    tableLayout: "fixed",
    width: "100%"
  } : {
    width: "max-content",
    minWidth: "100%"
  };
  if (!Array.isArray(columns) || !Array.isArray(rows) || !Array.isArray(columnWidths)) {
    console.warn("MintTable received invalid props:", {
      columns,
      rows,
      columnWidths
    });
  }
  if (!safeColumns.length && !safeRows.length) {
    return null;
  }
  return <div className="mint-table-container" style={containerStyle}>
      <table style={tableStyle}>
        {hasColumnWidths && <colgroup>
            {safeColumns.map((_, idx) => {
    const style = getColumnStyle(idx);
    return <col key={idx} style={style} />;
  })}
          </colgroup>}
        <thead>
          <tr>
            {safeColumns.map((col, idx) => <th key={idx} className="text-left" style={getColumnStyle(idx)}>
                <b>{parseMarkdown(col)}</b>
              </th>)}
          </tr>
        </thead>
        <tbody>
          {safeRows.map((row, rIdx) => {
    const safeRow = Array.isArray(row) ? row : [];
    return <tr key={rIdx}>
                {safeRow.map((cell, cIdx) => <td key={cIdx} style={getColumnStyle(cIdx)}>
                    {parseMarkdown(cell)}
                  </td>)}
              </tr>;
  })}
        </tbody>
      </table>
    </div>;
};

export const SiftIcon = ({className}) => <span className={`inline-flex items-center align-middle text-black dark:text-white ${className || ''}`}>
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Artwork" x="0px" y="0px" viewBox="0 0 1005.58 733.96" style={{
  enableBackground: "new 0 0 1005.58 733.96",
  width: "2em",
  height: "2em"
}} xmlSpace="preserve">
      <path fill="currentColor" d="M552.16,150.89c-165.6,0-180.29,160.61-300.62,192.32v2.67h601.24v-2.67C747.74,324.18,717.72,150.89,552.16,150.89z   M453.46,583.08c165.6,0,180.29-160.61,300.62-192.32v-2.67H152.84v2.67C257.88,409.78,287.91,583.08,453.46,583.08z" />
    </svg>
  </span>;

## Overview

Before learning how to visualize historical data in Sift, this step introduces the different methods available for **importing historical (backfill) datasets**. Historical data refers to data that was collected in the past and uploaded to Sift after the fact, rather than streamed in real time.

## Options

Sift supports multiple methods for importing historical data. Each method is suited to different workflows and supports specific file formats, as shown below:

<MintTable
  columns={['Method', 'Formats', 'When to use']}
  columnWidths={['5%', '5%', '10%']}
  rows={[
[
  'UI',
  'CSV \n\nTDMS \n\nParquet \n\nHDF5',
  'Use this option to manually upload historical or backfill data through the Sift web interface. \n\nIdeal for one-off uploads, quick validation, or smaller datasets where a point-and-click workflow is preferred.'
],
[
  'Python client library (`sift_client`)',
  'CSV \n\nTDMS \n\nParquet \n\nHDF5 \n\nTestStand XML (parsed)',
  'Use this option to programmatically upload historical data using Python. \n\nWell-suited for automated or repeatable batch workflows, or formats that require preprocessing before ingestion.'
],
[
  'REST API',
  'CSV \n\nTDMS \n\nParquet \n\nHDF5 \n\nJSONL',
  'Use this option to upload historical or backfill data programmatically over an HTTP REST interface. \n\nIdeal for integrating batch ingestion into pipelines, CI jobs, or non-Python systems.'
],
[
  'CLI (`sift_cli`)',
  'CSV \n\nParquet \n\nHDF5',
  'Use this option to upload historical data from the command line using the Sift CLI. \n\nSuitable for scripted or ad-hoc historical uploads without writing application code.'
],
]}
/>
