diff --git a/src/hammer.c b/src/hammer.c
index 443c77b790b10b5592958a55e7d457bc695c030a..991008d2fe1c1e0790faab402a87697a641d26e2 100644
--- a/src/hammer.c
+++ b/src/hammer.c
@@ -43,6 +43,7 @@ typedef struct {
 
 
 
+#define DEFAULT_ENDIANNESS (BIT_BIG_ENDIAN | BYTE_BIG_ENDIAN)
 
 HParseResult* h_parse(const HParser* parser, const uint8_t* input, size_t length) {
   return h_parse__m(&system_allocator, parser, input, length);
@@ -53,7 +54,7 @@ HParseResult* h_parse__m(HAllocator* mm__, const HParser* parser, const uint8_t*
     .index = 0,
     .bit_offset = 0,
     .overrun = 0,
-    .endianness = BIT_BIG_ENDIAN | BYTE_BIG_ENDIAN,
+    .endianness = DEFAULT_ENDIANNESS,
     .length = length,
     .input = input
   };
@@ -96,3 +97,59 @@ int h_compile__m(HAllocator* mm__, HParser* parser, HParserBackend backend, cons
     parser->backend = backend;
   return ret;
 }
+
+
+HSuspendedParser* h_parse_start(const HParser* parser) {
+  return h_parse_start__m(&system_allocator, parser);
+}
+HSuspendedParser* h_parse_start__m(HAllocator* mm__, const HParser* parser) {
+  if(!backends[parser->backend]->parse_start)
+    return NULL;
+
+  // allocate and init suspended state
+  HSuspendedParser *s = h_new(HSuspendedParser, 1);
+  if(!s)
+    return NULL;
+  s->mm__ = mm__;
+  s->parser = parser;
+  s->backend_state = NULL;
+  s->endianness = DEFAULT_ENDIANNESS;
+
+  // backend-specific initialization
+  // should allocate s->backend_state
+  backends[parser->backend]->parse_start(s);
+
+  return s;
+}
+
+bool h_parse_chunk(HSuspendedParser* s, const uint8_t* input, size_t length) {
+  assert(backends[s->parser->backend]->parse_chunk != NULL);
+
+  // input 
+  HInputStream input_stream = {
+    .index = 0,
+    .bit_offset = 0,
+    .overrun = 0,
+    .endianness = s->endianness,
+    .length = length,
+    .input = input
+  };
+
+  // process chunk
+  backends[s->parser->backend]->parse_chunk(s, &input_stream);
+  s->endianness = input_stream.endianness;
+
+  return !input_stream.overrun; // parser wants no more input? done.
+}
+
+HParseResult* h_parse_finish(HSuspendedParser* s) {
+  assert(backends[s->parser->backend]->parse_finish != NULL);
+
+  HAllocator *mm__ = s->mm__;
+
+  HParseResult *r = backends[s->parser->backend]->parse_finish(s);
+    // NB: backend should have freed backend_state
+  h_free(s);
+
+  return r;
+}
diff --git a/src/hammer.h b/src/hammer.h
index 42c73458a4d0e513f4400e1a3c6790e9cc736a9e..1be297c7a3b1230f2595ba47366a6591946b8777 100644
--- a/src/hammer.h
+++ b/src/hammer.h
@@ -140,6 +140,8 @@ typedef struct HParser_ {
   HCFChoice *desugared; /* if the parser can be desugared, its desugared form */
 } HParser;
 
+typedef struct HSuspendedParser_ HSuspendedParser;
+
 /**
  * Type of an action to apply to an AST, used in the action() parser. 
  * It can be any (user-defined) function that takes a HParseResult*
@@ -265,6 +267,27 @@ typedef struct HBenchmarkResults_ {
  */
 HAMMER_FN_DECL(HParseResult*, h_parse, const HParser* parser, const uint8_t* input, size_t length);
 
+/**
+ * Initialize a parser for iteratively consuming an input stream in chunks.
+ * This is only supported by some backends.
+ *
+ * Result is NULL if not supported by the backend.
+ */
+HAMMER_FN_DECL(HSuspendedParser*, h_parse_start, const HParser* parser);
+
+/**
+ * Run a suspended parser (as returned by h_parse_start) on a chunk of input.
+ *
+ * Returns true if the parser is done (needs no more input).
+ */
+bool h_parse_chunk(HSuspendedParser* s, const uint8_t* input, size_t length);
+
+/**
+ * Finish an iterative parse. Signals the end of input to the backend and
+ * returns the parse result.
+ */
+HParseResult* h_parse_finish(HSuspendedParser* s);
+
 /**
  * Given a string, returns a parser that parses that string value. 
  * 
diff --git a/src/internal.h b/src/internal.h
index 9aac4ee7dbaa4c4a1b8e87785b98f22265f37c71..8c799765fb1dfed1bcb7f7a22116f44bd08453b9 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -210,10 +210,28 @@ struct HParseState_ {
   HSlist *symbol_table; // its contents are HHashTables
 };
 
+struct HSuspendedParser_ {
+  HAllocator *mm__;
+  const HParser *parser;
+  void *backend_state;
+
+  // the only part of HInputStream that carries across chunks
+  uint8_t endianness;
+};
+
 typedef struct HParserBackendVTable_ {
   int (*compile)(HAllocator *mm__, HParser* parser, const void* params);
   HParseResult* (*parse)(HAllocator *mm__, const HParser* parser, HInputStream* stream);
   void (*free)(HParser* parser);
+
+  void (*parse_start)(HSuspendedParser *s);
+    // parse_start should allocate backend_state.
+  void (*parse_chunk)(HSuspendedParser *s, HInputStream *input);
+    // when parse_chunk leaves input.overrun unset, parse is done. else:
+    // parse_chunk MUST consume all input, integrating it into backend_state.
+    // calling parse_chunk again after parse is done should have no effect.
+  HParseResult *(*parse_finish)(HSuspendedParser *s);
+    // parse_finish must free backend_state.
 } HParserBackendVTable;