diff --git a/Makefile.am b/Makefile.am
index 10d6a9a..941d7e8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -16,6 +16,12 @@ EXTRA_DIST = \
 	LICENSES/LGPL-3.0-or-later.txt \
 	LICENSES/BSD-3-Clause.txt
 
+if WITH_EXAMPLES
+
+SUBDIRS += examples
+
+endif
+
 if WITH_TOOLS
 
 SUBDIRS += tools man
diff --git a/configure.ac b/configure.ac
index c1005a9..dde2fa5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -268,6 +268,7 @@ AC_CONFIG_FILES([Makefile
 		 lib/Makefile
 		 lib/libgpiod.pc
 		 contrib/Makefile
+		 examples/Makefile
 		 tools/Makefile
 		 tests/Makefile
 		 tests/gpiosim/Makefile
diff --git a/examples/.gitignore b/examples/.gitignore
new file mode 100644
index 0000000..bdfde9a
--- /dev/null
+++ b/examples/.gitignore
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2023 Kent Gibson <warthog618@gmail.com>
+
+async_watch_line_value
+get_line_value
+toggle_line_value
+watch_line_value
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 0000000..4ad124b
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2023 Kent Gibson <warthog618@gmail.com>
+
+AM_CFLAGS = -I$(top_srcdir)/include/ -include $(top_builddir)/config.h
+AM_CFLAGS += -Wall -Wextra -g -std=gnu89
+
+LDADD = $(top_builddir)/lib/libgpiod.la
+
+bin_PROGRAMS = async_watch_line_value get_line_value toggle_line_value watch_line_value
+
+async_watch_line_value_SOURCES = async_watch_line_value.c
+
+get_line_value_SOURCES = get_line_value.c
+
+toggle_line_valuer_SOURCES = toggle_line_value.c
+
+watch_line_value_SOURCES = watch_line_value.c
diff --git a/examples/async_watch_line_value.c b/examples/async_watch_line_value.c
new file mode 100644
index 0000000..de15c97
--- /dev/null
+++ b/examples/async_watch_line_value.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023 Kent Gibson <warthog618@gmail.com>
+
+/* Minimal example of asynchronously watching for edges on a single line */
+
+#include <errno.h>
+#include <gpiod.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* request a line as input with edge detection */
+struct gpiod_line_request *request_input_line(const char *chip_path,
+					      unsigned int offset,
+					      const char *consumer)
+{
+	struct gpiod_line_settings *settings;
+	struct gpiod_line_request *request = NULL;
+	struct gpiod_request_config *req_cfg = NULL;
+	struct gpiod_line_config *line_cfg;
+	struct gpiod_chip *chip;
+	int ret;
+
+	chip = gpiod_chip_open(chip_path);
+	if (!chip)
+		return NULL;
+
+	settings = gpiod_line_settings_new();
+	if (!settings)
+		goto close_chip;
+
+	gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_INPUT);
+	gpiod_line_settings_set_edge_detection(settings, GPIOD_LINE_EDGE_BOTH);
+	/* assume a button connecting the pin to ground, so pull it up... */
+	gpiod_line_settings_set_bias(settings, GPIOD_LINE_BIAS_PULL_UP);
+	/* ... and provide some debounce. */
+	gpiod_line_settings_set_debounce_period_us(settings, 10000);
+
+	line_cfg = gpiod_line_config_new();
+	if (!line_cfg)
+		goto free_settings;
+
+	ret = gpiod_line_config_add_line_settings(line_cfg, &offset, 1,
+						  settings);
+	if (ret)
+		goto free_line_config;
+
+	if (consumer) {
+		req_cfg = gpiod_request_config_new();
+		if (!req_cfg)
+			goto free_line_config;
+
+		gpiod_request_config_set_consumer(req_cfg, consumer);
+	}
+
+	request = gpiod_chip_request_lines(chip, req_cfg, line_cfg);
+
+	gpiod_request_config_free(req_cfg);
+free_line_config:
+	gpiod_line_config_free(line_cfg);
+free_settings:
+	gpiod_line_settings_free(settings);
+close_chip:
+	gpiod_chip_close(chip);
+
+	return request;
+}
+
+const char *edge_event_type_str(struct gpiod_edge_event *event)
+{
+	enum gpiod_edge_event_type eet = gpiod_edge_event_get_event_type(event);
+
+	if (eet == GPIOD_EDGE_EVENT_RISING_EDGE)
+		return "Rising ";
+	if (eet == GPIOD_EDGE_EVENT_FALLING_EDGE)
+		return "Falling";
+	return "Unknown";
+}
+
+int main(void)
+{
+	struct gpiod_line_request *request;
+	struct gpiod_edge_event_buffer *event_buffer;
+	struct gpiod_edge_event *event;
+	struct pollfd pollfd;
+	int i, ret, event_buf_size;
+	/* example configuration - customize to suit your situation */
+	const char *chip_path = "/dev/gpiochip0";
+	int line_offset = 5;
+
+	request = request_input_line(chip_path, line_offset,
+				     "async-watch-line-value");
+	if (!request) {
+		fprintf(stderr, "failed to request line: %s\n",
+			strerror(errno));
+		return EXIT_FAILURE;
+	}
+
+	/* a larger buffer is an optimisation for reading bursts of events from
+	 * the kernel, but that is not necessary in this case, so 1 is fine.
+	 */
+	event_buf_size = 1;
+	event_buffer = gpiod_edge_event_buffer_new(event_buf_size);
+	if (!event_buffer) {
+		fprintf(stderr, "failed to create event buffer: %s\n",
+			strerror(errno));
+		return EXIT_FAILURE;
+	}
+
+	pollfd.fd = gpiod_line_request_get_fd(request);
+	pollfd.events = POLLIN;
+	while (1) {
+		ret = poll(&pollfd, 1, -1);
+		if (ret == -1) {
+			fprintf(stderr, "error waiting for edge events: %s\n",
+				strerror(errno));
+			return EXIT_FAILURE;
+		}
+		ret = gpiod_line_request_read_edge_events(request, event_buffer,
+							  event_buf_size);
+		if (ret == -1) {
+			fprintf(stderr, "error reading edge events: %s\n",
+				strerror(errno));
+			return EXIT_FAILURE;
+		}
+		for (i = 0; i < ret; i++) {
+			event = gpiod_edge_event_buffer_get_event(event_buffer,
+								  i);
+			printf("offset: %d, type: %s, event #%ld\n",
+			       gpiod_edge_event_get_line_offset(event),
+			       edge_event_type_str(event),
+			       gpiod_edge_event_get_line_seqno(event));
+		}
+	}
+}
diff --git a/examples/get_line_value.c b/examples/get_line_value.c
new file mode 100644
index 0000000..fc96a5b
--- /dev/null
+++ b/examples/get_line_value.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023 Kent Gibson <warthog618@gmail.com>
+
+/* Minimal example of reading a single line. */
+
+#include <errno.h>
+#include <gpiod.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* request a line as input */
+struct gpiod_line_request *request_input_line(const char *chip_path,
+					      unsigned int offset,
+					      const char *consumer)
+{
+	struct gpiod_line_settings *settings;
+	struct gpiod_line_request *request = NULL;
+	struct gpiod_request_config *req_cfg = NULL;
+	struct gpiod_line_config *line_cfg;
+	struct gpiod_chip *chip;
+	int ret;
+
+	chip = gpiod_chip_open(chip_path);
+	if (!chip)
+		return NULL;
+
+	settings = gpiod_line_settings_new();
+	if (!settings)
+		goto close_chip;
+
+	gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_INPUT);
+
+	line_cfg = gpiod_line_config_new();
+	if (!line_cfg)
+		goto free_settings;
+
+	ret = gpiod_line_config_add_line_settings(line_cfg, &offset, 1,
+						  settings);
+	if (ret)
+		goto free_line_config;
+
+	if (consumer) {
+		req_cfg = gpiod_request_config_new();
+		if (!req_cfg)
+			goto free_line_config;
+
+		gpiod_request_config_set_consumer(req_cfg, consumer);
+	}
+
+	request = gpiod_chip_request_lines(chip, req_cfg, line_cfg);
+
+	gpiod_request_config_free(req_cfg);
+free_line_config:
+	gpiod_line_config_free(line_cfg);
+free_settings:
+	gpiod_line_settings_free(settings);
+close_chip:
+	gpiod_chip_close(chip);
+
+	return request;
+}
+
+int print_value(enum gpiod_line_value value)
+{
+	if (value == GPIOD_LINE_VALUE_ACTIVE)
+		printf("Active\n");
+	else if (value == GPIOD_LINE_VALUE_INACTIVE) {
+		printf("Inactive\n");
+	} else {
+		fprintf(stderr, "error reading value: %s\n",
+			strerror(errno));
+		return EXIT_FAILURE;
+	}
+	return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+	struct gpiod_line_request *request;
+	enum gpiod_line_value value;
+	int ret;
+	/* example configuration - customize to suit your situation */
+	const char *chip_path = "/dev/gpiochip0";
+	int line_offset = 5;
+
+	request = request_input_line(chip_path, line_offset, "get-line-value");
+	if (!request) {
+		fprintf(stderr, "failed to request line: %s\n",
+			strerror(errno));
+		return EXIT_FAILURE;
+	}
+
+	value = gpiod_line_request_get_value(request, line_offset);
+	ret = print_value(value);
+	return ret;
+}
diff --git a/examples/toggle_line_value.c b/examples/toggle_line_value.c
new file mode 100644
index 0000000..ada163e
--- /dev/null
+++ b/examples/toggle_line_value.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023 Kent Gibson <warthog618@gmail.com>
+
+/* Minimal example of toggling a single line. */
+
+#include <errno.h>
+#include <gpiod.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct gpiod_line_request *request_output_line(const char *chip_path,
+					       unsigned int offset,
+					       enum gpiod_line_value value,
+					       const char *consumer)
+{
+	struct gpiod_line_settings *settings;
+	struct gpiod_line_request *request = NULL;
+	struct gpiod_request_config *req_cfg = NULL;
+	struct gpiod_line_config *line_cfg;
+	struct gpiod_chip *chip;
+	int ret;
+
+	chip = gpiod_chip_open(chip_path);
+	if (!chip)
+		return NULL;
+
+	settings = gpiod_line_settings_new();
+	if (!settings)
+		goto close_chip;
+
+	gpiod_line_settings_set_direction(settings,
+					  GPIOD_LINE_DIRECTION_OUTPUT);
+	gpiod_line_settings_set_output_value(settings, value);
+
+	line_cfg = gpiod_line_config_new();
+	if (!line_cfg)
+		goto free_settings;
+
+	ret = gpiod_line_config_add_line_settings(line_cfg, &offset, 1,
+						  settings);
+	if (ret)
+		goto free_settings;
+
+	if (consumer) {
+		req_cfg = gpiod_request_config_new();
+		if (!req_cfg)
+			goto free_line_config;
+
+		gpiod_request_config_set_consumer(req_cfg, consumer);
+	}
+
+	request = gpiod_chip_request_lines(chip, req_cfg, line_cfg);
+
+	gpiod_request_config_free(req_cfg);
+free_line_config:
+	gpiod_line_config_free(line_cfg);
+free_settings:
+	gpiod_line_settings_free(settings);
+close_chip:
+	gpiod_chip_close(chip);
+
+	return request;
+}
+
+enum gpiod_line_value toggle_line_value(enum gpiod_line_value value)
+{
+	return (value == GPIOD_LINE_VALUE_ACTIVE) ? GPIOD_LINE_VALUE_INACTIVE :
+						    GPIOD_LINE_VALUE_ACTIVE;
+}
+
+void print_value(enum gpiod_line_value value)
+{
+	if (value == GPIOD_LINE_VALUE_ACTIVE)
+		printf("Active\n");
+	else
+		printf("Inactive\n");
+}
+
+int main(void)
+{
+	struct gpiod_line_request *request;
+	enum gpiod_line_value value = GPIOD_LINE_VALUE_ACTIVE;
+	/* example configuration - customize to suit your situation */
+	const char *chip_path = "/dev/gpiochip0";
+	int line_offset = 5;
+
+	request = request_output_line(chip_path, line_offset, value,
+				      "toggle-line-value");
+	if (!request) {
+		fprintf(stderr, "failed to request line: %s\n",
+			strerror(errno));
+		return EXIT_FAILURE;
+	}
+
+	while (1) {
+		print_value(value);
+		sleep(1);
+		value = toggle_line_value(value);
+		gpiod_line_request_set_value(request, line_offset, value);
+	}
+
+	gpiod_line_request_release(request);
+	return EXIT_SUCCESS;
+}
diff --git a/examples/watch_line_value.c b/examples/watch_line_value.c
new file mode 100644
index 0000000..011943e
--- /dev/null
+++ b/examples/watch_line_value.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023 Kent Gibson <warthog618@gmail.com>
+
+/* Minimal example of watching for edges on a single line. */
+
+#include <errno.h>
+#include <gpiod.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* request a line as input with edge detection */
+struct gpiod_line_request *request_input_line(const char *chip_path,
+					      unsigned int offset,
+					      const char *consumer)
+{
+	struct gpiod_line_settings *settings;
+	struct gpiod_line_request *request = NULL;
+	struct gpiod_request_config *req_cfg = NULL;
+	struct gpiod_line_config *line_cfg;
+	struct gpiod_chip *chip;
+	int ret;
+
+	chip = gpiod_chip_open(chip_path);
+	if (!chip)
+		return NULL;
+
+	settings = gpiod_line_settings_new();
+	if (!settings)
+		goto close_chip;
+
+	gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_INPUT);
+	gpiod_line_settings_set_edge_detection(settings, GPIOD_LINE_EDGE_BOTH);
+	/* assume a button connecting the pin to ground, so pull it up... */
+	gpiod_line_settings_set_bias(settings, GPIOD_LINE_BIAS_PULL_UP);
+	/* ... and provide some debounce. */
+	gpiod_line_settings_set_debounce_period_us(settings, 10000);
+
+	line_cfg = gpiod_line_config_new();
+	if (!line_cfg)
+		goto free_settings;
+
+	ret = gpiod_line_config_add_line_settings(line_cfg, &offset, 1,
+						  settings);
+	if (ret)
+		goto free_line_config;
+
+	if (consumer) {
+		req_cfg = gpiod_request_config_new();
+		if (!req_cfg)
+			goto free_line_config;
+
+		gpiod_request_config_set_consumer(req_cfg, consumer);
+	}
+
+	request = gpiod_chip_request_lines(chip, req_cfg, line_cfg);
+
+	gpiod_request_config_free(req_cfg);
+free_line_config:
+	gpiod_line_config_free(line_cfg);
+free_settings:
+	gpiod_line_settings_free(settings);
+close_chip:
+	gpiod_chip_close(chip);
+
+	return request;
+}
+
+const char *edge_event_type_str(struct gpiod_edge_event *event)
+{
+	enum gpiod_edge_event_type eet = gpiod_edge_event_get_event_type(event);
+
+	if (eet == GPIOD_EDGE_EVENT_RISING_EDGE)
+		return "Rising ";
+	if (eet == GPIOD_EDGE_EVENT_FALLING_EDGE)
+		return "Falling";
+	return "Unknown";
+}
+
+int main(void)
+{
+	struct gpiod_line_request *request;
+	struct gpiod_edge_event_buffer *event_buffer;
+	struct gpiod_edge_event *event;
+	int i, ret, event_buf_size;
+	/* example configuration - customize to suit your situation */
+	const char *chip_path = "/dev/gpiochip0";
+	int line_offset = 5;
+
+	request = request_input_line(chip_path, line_offset,
+				     "watch-line-value");
+	if (!request) {
+		fprintf(stderr, "failed to request line: %s\n",
+			strerror(errno));
+		return EXIT_FAILURE;
+	}
+
+	/* a larger buffer is an optimisation for reading bursts of events from
+	 * the kernel, but that is not necessary in this case, so 1 is fine.
+	 */
+	event_buf_size = 1;
+	event_buffer = gpiod_edge_event_buffer_new(event_buf_size);
+	if (!event_buffer) {
+		fprintf(stderr, "failed to create event buffer: %s\n",
+			strerror(errno));
+		return EXIT_FAILURE;
+	}
+
+	while (1) {
+		/* blocks until at least one event is available */
+		ret = gpiod_line_request_read_edge_events(request, event_buffer,
+							  event_buf_size);
+		if (ret == -1) {
+			fprintf(stderr, "error reading edge events: %s\n",
+				strerror(errno));
+			return EXIT_FAILURE;
+		}
+		for (i = 0; i < ret; i++) {
+			event = gpiod_edge_event_buffer_get_event(event_buffer,
+								  i);
+			printf("offset: %d, type: %s, event #%ld\n",
+			       gpiod_edge_event_get_line_offset(event),
+			       edge_event_type_str(event),
+			       gpiod_edge_event_get_line_seqno(event));
+		}
+	}
+}
